Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-08-26 15:11:58 +00:00
parent 93c1e0e4c2
commit 485728af8d
39 changed files with 167 additions and 68 deletions

View File

@ -0,0 +1 @@
# This empty file is used for agent-based integration with Kubernetes

View File

@ -109,7 +109,7 @@ export default {
v-for="template in templates" v-for="template in templates"
:key="template.key" :key="template.key"
data-qa-selector="incident_templates_item" data-qa-selector="incident_templates_item"
:is-check-item="true" is-check-item
:is-checked="isTemplateSelected(template.key)" :is-checked="isTemplateSelected(template.key)"
@click="selectIssueTemplate(template.key)" @click="selectIssueTemplate(template.key)"
> >

View File

@ -40,7 +40,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="project in projects" v-for="project in projects"
:key="project.id" :key="project.id"
:is-check-item="true" is-check-item
:is-checked="project.id === selectedProject.id" :is-checked="project.id === selectedProject.id"
@click="selectProject(project)" @click="selectProject(project)"
> >

View File

@ -182,7 +182,7 @@ export default {
</template> </template>
<template v-if="!showCustomLanguageInput" #highlighted-items> <template v-if="!showCustomLanguageInput" #highlighted-items>
<gl-dropdown-item :key="selectedLanguage.syntax" is-check-item :is-checked="true"> <gl-dropdown-item :key="selectedLanguage.syntax" is-check-item is-checked>
{{ selectedLanguage.label }} {{ selectedLanguage.label }}
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>

View File

@ -73,8 +73,8 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="(version, index) in allVersions" v-for="(version, index) in allVersions"
:key="version.id" :key="version.id"
:is-check-item="true" is-check-item
:is-check-centered="true" is-check-centered
:is-checked="findVersionId(version.id) === currentVersionId" :is-checked="findVersionId(version.id) === currentVersionId"
:avatar-url="getAvatarUrl(version)" :avatar-url="getAvatarUrl(version)"
@click="routeToVersion(version.id)" @click="routeToVersion(version.id)"

View File

@ -37,7 +37,7 @@ export default {
:class="{ :class="{
'is-active': version.selected, 'is-active': version.selected,
}" }"
:is-check-item="true" is-check-item
:is-checked="version.selected" :is-checked="version.selected"
:href="version.href" :href="version.href"
> >

View File

@ -50,7 +50,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="mode in modeDropdownItems" v-for="mode in modeDropdownItems"
:key="mode.viewerType" :key="mode.viewerType"
:is-check-item="true" is-check-item
:is-checked="viewer === mode.viewerType" :is-checked="viewer === mode.viewerType"
@click="changeMode(mode.viewerType)" @click="changeMode(mode.viewerType)"
> >

View File

@ -243,7 +243,7 @@ export default {
v-for="(item, idx) in extraLinks" v-for="(item, idx) in extraLinks"
:key="idx" :key="idx"
:href="item.url" :href="item.url"
:is-check-item="true" is-check-item
data-testid="milestone-combobox-extra-links" data-testid="milestone-combobox-extra-links"
> >
{{ item.text }} {{ item.text }}

View File

@ -202,7 +202,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="environment in filteredEnvironments" v-for="environment in filteredEnvironments"
:key="environment.id" :key="environment.id"
:is-check-item="true" is-check-item
:is-checked="environment.name === currentEnvironmentName" :is-checked="environment.name === currentEnvironmentName"
:href="getEnvironmentPath(environment.id)" :href="getEnvironmentPath(environment.id)"
> >

View File

@ -86,7 +86,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="dashboard in starredDashboards" v-for="dashboard in starredDashboards"
:key="dashboard.path" :key="dashboard.path"
:is-check-item="true" is-check-item
:is-checked="dashboard.path === selectedDashboardPath" :is-checked="dashboard.path === selectedDashboardPath"
@click="selectDashboard(dashboard)" @click="selectDashboard(dashboard)"
> >
@ -105,7 +105,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="dashboard in nonStarredDashboards" v-for="dashboard in nonStarredDashboards"
:key="dashboard.path" :key="dashboard.path"
:is-check-item="true" is-check-item
:is-checked="dashboard.path === selectedDashboardPath" :is-checked="dashboard.path === selectedDashboardPath"
@click="selectDashboard(dashboard)" @click="selectDashboard(dashboard)"
> >

View File

@ -163,7 +163,7 @@ export default {
:text="dropdownText" :text="dropdownText"
> >
<gl-dropdown-item <gl-dropdown-item
:is-check-item="true" is-check-item
:is-checked="refreshInterval === null" :is-checked="refreshInterval === null"
@click="removeRefreshInterval()" @click="removeRefreshInterval()"
>{{ __('Off') }}</gl-dropdown-item >{{ __('Off') }}</gl-dropdown-item
@ -172,7 +172,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="(option, i) in $options.refreshIntervals" v-for="(option, i) in $options.refreshIntervals"
:key="i" :key="i"
:is-check-item="true" is-check-item
:is-checked="isChecked(option)" :is-checked="isChecked(option)"
@click="setRefreshInterval(option)" @click="setRefreshInterval(option)"
>{{ option.label }}</gl-dropdown-item >{{ option.label }}</gl-dropdown-item

View File

@ -177,7 +177,7 @@ export default {
v-for="{ text, key, cls } in $options.SORT_OPTIONS" v-for="{ text, key, cls } in $options.SORT_OPTIONS"
:key="text" :key="text"
:class="cls" :class="cls"
:is-check-item="true" is-check-item
:is-checked="isSortDropdownItemActive(key)" :is-checked="isSortDropdownItemActive(key)"
@click="fetchSortedDiscussions(key)" @click="fetchSortedDiscussions(key)"
> >
@ -192,7 +192,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="filter in filters" v-for="filter in filters"
:key="filter.value" :key="filter.value"
:is-check-item="true" is-check-item
:is-checked="filter.value === currentValue" :is-checked="filter.value === currentValue"
:class="{ 'is-active': filter.value === currentValue }" :class="{ 'is-active': filter.value === currentValue }"
:data-filter-type="filterType(filter.value)" :data-filter-type="filterType(filter.value)"

View File

@ -180,7 +180,7 @@ export default {
v-for="({ group_name }, index) in dailyCoverageData" v-for="({ group_name }, index) in dailyCoverageData"
:key="index" :key="index"
:value="group_name" :value="group_name"
:is-check-item="true" is-check-item
:is-checked="index === selectedCoverageIndex" :is-checked="index === selectedCoverageIndex"
@click="setSelectedCoverage(index)" @click="setSelectedCoverage(index)"
> >

View File

@ -237,7 +237,7 @@ export default {
v-for="branch in availableBranches" v-for="branch in availableBranches"
:key="branch" :key="branch"
:is-checked="currentBranch === branch" :is-checked="currentBranch === branch"
:is-check-item="true" is-check-item
data-qa-selector="branch_menu_item_button" data-qa-selector="branch_menu_item_button"
@click="selectBranch(branch)" @click="selectBranch(branch)"
> >

View File

@ -100,7 +100,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="template in item" v-for="template in item"
:key="template.key" :key="template.key"
:is-check-item="true" is-check-item
:is-checked=" :is-checked="
template.project_id === selectedFileTemplateProjectId && template.project_id === selectedFileTemplateProjectId &&
template.name === selectedTemplate template.name === selectedTemplate

View File

@ -144,9 +144,9 @@ export default {
/> />
<gl-dropdown-item <gl-dropdown-item
class="gl-border-b-solid gl-border-b-gray-100 gl-border-b-1 gl-pb-2! gl-mb-2" class="gl-border-b-solid gl-border-b-gray-100 gl-border-b-1 gl-pb-2! gl-mb-2"
:is-check-item="true" is-check-item
:is-checked="isSelected($options.ANY_OPTION)" :is-checked="isSelected($options.ANY_OPTION)"
:is-check-centered="true" is-check-centered
@click="updateDropdown($options.ANY_OPTION)" @click="updateDropdown($options.ANY_OPTION)"
> >
<span data-testid="item-title">{{ $options.ANY_OPTION.name }}</span> <span data-testid="item-title">{{ $options.ANY_OPTION.name }}</span>

View File

@ -53,9 +53,9 @@ export default {
<template> <template>
<gl-dropdown-item <gl-dropdown-item
:is-check-item="true" is-check-item
:is-checked="isSelected" :is-checked="isSelected"
:is-check-centered="true" is-check-centered
@click="$emit('change', item)" @click="$emit('change', item)"
> >
<div class="gl-display-flex gl-align-items-center"> <div class="gl-display-flex gl-align-items-center">

View File

@ -62,7 +62,7 @@ export default {
v-for="status in $options.STATUS_LIST" v-for="status in $options.STATUS_LIST"
:key="status" :key="status"
data-testid="status-dropdown-item" data-testid="status-dropdown-item"
:is-check-item="true" is-check-item
:is-checked="status === value" :is-checked="status === value"
@click="$emit('input', status)" @click="$emit('input', status)"
> >

View File

@ -179,7 +179,7 @@ export default {
v-for="option in severitiesList" v-for="option in severitiesList"
:key="option.value" :key="option.value"
data-testid="severityDropdownItem" data-testid="severityDropdownItem"
:is-check-item="true" is-check-item
:is-checked="option.value === severity" :is-checked="option.value === severity"
@click="updateSeverity(option.value)" @click="updateSeverity(option.value)"
> >

View File

@ -369,7 +369,7 @@ export default {
<gl-search-box-by-type ref="search" v-model="searchTerm" /> <gl-search-box-by-type ref="search" v-model="searchTerm" />
<gl-dropdown-item <gl-dropdown-item
:data-testid="`no-${formatIssuableAttribute.kebab}-item`" :data-testid="`no-${formatIssuableAttribute.kebab}-item`"
:is-check-item="true" is-check-item
:is-checked="isAttributeChecked($options.noAttributeId)" :is-checked="isAttributeChecked($options.noAttributeId)"
@click="updateAttribute($options.noAttributeId)" @click="updateAttribute($options.noAttributeId)"
> >
@ -396,7 +396,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="attrItem in attributesList" v-for="attrItem in attributesList"
:key="attrItem.id" :key="attrItem.id"
:is-check-item="true" is-check-item
:is-checked="isAttributeChecked(attrItem.id)" :is-checked="isAttributeChecked(attrItem.id)"
:data-testid="`${formatIssuableAttribute.kebab}-items`" :data-testid="`${formatIssuableAttribute.kebab}-items`"
@click="updateAttribute(attrItem.id)" @click="updateAttribute(attrItem.id)"

View File

@ -77,7 +77,7 @@ export default {
<template v-for="(action, index) in actions"> <template v-for="(action, index) in actions">
<gl-dropdown-item <gl-dropdown-item
:key="action.key" :key="action.key"
:is-check-item="true" is-check-item
:is-checked="action.key === selectedAction.key" :is-checked="action.key === selectedAction.key"
:secondary-text="action.secondaryText" :secondary-text="action.secondaryText"
:data-qa-selector="`${action.key}_menu_item`" :data-qa-selector="`${action.key}_menu_item`"

View File

@ -42,8 +42,8 @@ export default {
v-for="color in colors" v-for="color in colors"
:key="color.color" :key="color.color"
:is-checked="isColorSelected(color)" :is-checked="isColorSelected(color)"
:is-check-centered="true" is-check-centered
:is-check-item="true" is-check-item
@click.native.capture.stop="handleColorClick(color)" @click.native.capture.stop="handleColorClick(color)"
> >
<color-item :color="color.color" :title="color.title" /> <color-item :color="color.color" :title="color.title" />

View File

@ -149,8 +149,8 @@ export default {
v-for="option in presetOptions" v-for="option in presetOptions"
:key="option.id" :key="option.id"
:is-checked="isSelected(option)" :is-checked="isSelected(option)"
:is-check-centered="true" is-check-centered
:is-check-item="true" is-check-item
@click.native.capture.stop="selectOption(option)" @click.native.capture.stop="selectOption(option)"
> >
<slot name="preset-item" :item="option"> <slot name="preset-item" :item="option">

View File

@ -369,7 +369,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="sortBy in sortOptions" v-for="sortBy in sortOptions"
:key="sortBy.id" :key="sortBy.id"
:is-check-item="true" is-check-item
:is-checked="sortBy.id === selectedSortOption.id" :is-checked="sortBy.id === selectedSortOption.id"
@click="handleSortOptionClick(sortBy)" @click="handleSortOptionClick(sortBy)"
>{{ sortBy.title }}</gl-dropdown-item >{{ sortBy.title }}</gl-dropdown-item

View File

@ -49,7 +49,7 @@ export default {
v-for="option in parsedOptions" v-for="option in parsedOptions"
:key="option.value" :key="option.value"
:is-checked="option.selected" :is-checked="option.selected"
:is-check-item="true" is-check-item
@click="setSelected(option.value)" @click="setSelected(option.value)"
> >
{{ option.label }} {{ option.label }}

View File

@ -253,7 +253,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="architecture in architectures" v-for="architecture in architectures"
:key="architecture.name" :key="architecture.name"
:is-check-item="true" is-check-item
:is-checked="selectedArchitecture === architecture.name" :is-checked="selectedArchitecture === architecture.name"
data-testid="architecture-dropdown-item" data-testid="architecture-dropdown-item"
@click="selectArchitecture(architecture.name)" @click="selectArchitecture(architecture.name)"

View File

@ -180,7 +180,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-for="project in projects" v-for="project in projects"
:key="project.id" :key="project.id"
:is-check-item="true" is-check-item
:is-checked="isSelectedProject(project)" :is-checked="isSelectedProject(project)"
@click.stop.prevent="handleProjectSelect(project)" @click.stop.prevent="handleProjectSelect(project)"
>{{ project.name_with_namespace }}</gl-dropdown-item >{{ project.name_with_namespace }}</gl-dropdown-item

View File

@ -154,8 +154,8 @@ export default {
v-for="(label, index) in visibleLabels" v-for="(label, index) in visibleLabels"
:key="label.id" :key="label.id"
:is-checked="isLabelSelected(label)" :is-checked="isLabelSelected(label)"
:is-check-centered="true" is-check-centered
:is-check-item="true" is-check-item
:active="shouldHighlightFirstItem && index === 0" :active="shouldHighlightFirstItem && index === 0"
active-class="is-focused" active-class="is-focused"
data-testid="labels-list" data-testid="labels-list"

View File

@ -68,7 +68,7 @@ export default {
<template v-for="(item, itemIndex) in actionItems"> <template v-for="(item, itemIndex) in actionItems">
<gl-dropdown-item <gl-dropdown-item
:key="item.eventName" :key="item.eventName"
:is-check-item="true" is-check-item
:is-checked="selectedItem === item" :is-checked="selectedItem === item"
@click="changeSelectedItem(item)" @click="changeSelectedItem(item)"
> >

View File

@ -72,7 +72,7 @@ export default {
v-for="timezone in filteredResults" v-for="timezone in filteredResults"
:key="timezone.formattedTimezone" :key="timezone.formattedTimezone"
:is-checked="isSelected(timezone)" :is-checked="isSelected(timezone)"
:is-check-item="true" is-check-item
@click="selectTimezone(timezone)" @click="selectTimezone(timezone)"
> >
{{ timezone.formattedTimezone }} {{ timezone.formattedTimezone }}

View File

@ -320,7 +320,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-if="isSearchEmpty" v-if="isSearchEmpty"
:is-checked="selectedIsEmpty" :is-checked="selectedIsEmpty"
:is-check-centered="true" is-check-centered
data-testid="unassign" data-testid="unassign"
@click.native.capture.stop="$emit('input', [])" @click.native.capture.stop="$emit('input', [])"
> >

View File

@ -2248,6 +2248,19 @@ Returned object:
} }
``` ```
## Remove a project avatar
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92604) in GitLab 15.4.
To remove a project avatar, use a blank value for the `avatar` attribute.
Example request:
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \
--data "avatar=" "https://gitlab.example.com/api/v4/projects/5"
```
## Share project with group ## Share project with group
Allow to share project with group. Allow to share project with group.

View File

@ -451,3 +451,12 @@ test-job:
reports: reports:
dotenv: build.env dotenv: build.env
``` ```
### Job artifacts are not expired
If some job artifacts are not expiring as expected, check if the
[**Keep artifacts from most recent successful jobs**](#keep-artifacts-from-most-recent-successful-jobs)
setting is enabled.
When this setting is enabled, job artifacts from the latest successful pipeline
of each ref do not expire and are not deleted.

View File

@ -33,6 +33,9 @@ GitLab Shell handles Git SSH sessions for GitLab and modifies the list of author
For more information, [refer to the README](https://gitlab.com/gitlab-org/gitlab-shell/-/blob/main/README.md). For more information, [refer to the README](https://gitlab.com/gitlab-org/gitlab-shell/-/blob/main/README.md).
for GitLab Shell. for GitLab Shell.
To learn about the reasoning behind our creation of `gitlab-sshd`, read the blog post
[Why we implemented our own SSHD solution](https://about.gitlab.com/blog/2022/08/17/why-we-have-implemented-our-own-sshd-solution-on-gitlab-sass/).
## GitLab Rails ## GitLab Rails
### Gitaly touch points ### Gitaly touch points

View File

@ -58,7 +58,7 @@ module API
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Deprecated: Use :topics instead' optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Deprecated: Use :topics instead'
optional :topics, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of topics for a project' optional :topics, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of topics for a project'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960 # TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
optional :avatar, type: File, desc: 'Avatar image for project' # rubocop:disable Scalability/FileUploads optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for project'
optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line' optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line'
optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests' optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests'
optional :suggestion_commit_message, type: String, desc: 'The commit message used to apply merge request suggestions' optional :suggestion_commit_message, type: String, desc: 'The commit message used to apply merge request suggestions'

View File

@ -453,6 +453,8 @@ module API
filter_attributes_using_license!(attrs) filter_attributes_using_license!(attrs)
verify_update_project_attrs!(user_project, attrs) verify_update_project_attrs!(user_project, attrs)
user_project.remove_avatar! if attrs.key?(:avatar) && attrs[:avatar].nil?
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
if result[:status] == :success if result[:status] == :success

View File

@ -48,6 +48,7 @@ end
RSpec.describe API::Projects do RSpec.describe API::Projects do
include ProjectForksHelper include ProjectForksHelper
include WorkhorseHelpers
include StubRequests include StubRequests
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
@ -1349,7 +1350,12 @@ RSpec.describe API::Projects do
it 'uploads avatar for project a project' do it 'uploads avatar for project a project' do
project = attributes_for(:project, avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif')) project = attributes_for(:project, avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif'))
post api('/projects', user), params: project workhorse_form_with_file(
api('/projects', user),
method: :post,
file_key: :avatar,
params: project
)
project_id = json_response['id'] project_id = json_response['id']
expect(json_response['avatar_url']).to eq("http://localhost/uploads/-/system/project/avatar/#{project_id}/banana_sample.gif") expect(json_response['avatar_url']).to eq("http://localhost/uploads/-/system/project/avatar/#{project_id}/banana_sample.gif")
@ -1925,8 +1931,6 @@ RSpec.describe API::Projects do
end end
describe "POST /projects/:id/uploads/authorize" do describe "POST /projects/:id/uploads/authorize" do
include WorkhorseHelpers
let(:headers) { workhorse_internal_api_request_header.merge({ 'HTTP_GITLAB_WORKHORSE' => 1 }) } let(:headers) { workhorse_internal_api_request_header.merge({ 'HTTP_GITLAB_WORKHORSE' => 1 }) }
context 'with authorized user' do context 'with authorized user' do
@ -3584,18 +3588,77 @@ RSpec.describe API::Projects do
end end
end end
it 'updates avatar' do context 'with changes to the avatar' do
project_param = { let_it_be(:avatar_file) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') }
avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', let_it_be(:alternate_avatar_file) { fixture_file_upload('spec/fixtures/rails_sample.png', 'image/png') }
'image/gif') let_it_be(:project_with_avatar, reload: true) do
} create(:project,
:private,
:repository,
name: 'project-with-avatar',
creator_id: user.id,
namespace: user.namespace,
avatar: avatar_file)
end
put api("/projects/#{project3.id}", user), params: project_param it 'uploads avatar to project without an avatar' do
workhorse_form_with_file(
api("/projects/#{project3.id}", user),
method: :put,
file_key: :avatar,
params: { avatar: avatar_file }
)
expect(response).to have_gitlab_http_status(:ok) aggregate_failures "testing response" do
expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\ expect(response).to have_gitlab_http_status(:ok)
'-/system/project/avatar/'\ expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\
"#{project3.id}/banana_sample.gif") '-/system/project/avatar/'\
"#{project3.id}/banana_sample.gif")
end
end
it 'uploads and changes avatar to project with an avatar' do
workhorse_form_with_file(
api("/projects/#{project_with_avatar.id}", user),
method: :put,
file_key: :avatar,
params: { avatar: alternate_avatar_file }
)
aggregate_failures "testing response" do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\
'-/system/project/avatar/'\
"#{project_with_avatar.id}/rails_sample.png")
end
end
it 'uploads and changes avatar to project among other changes' do
workhorse_form_with_file(
api("/projects/#{project_with_avatar.id}", user),
method: :put,
file_key: :avatar,
params: { description: 'changed description', avatar: avatar_file }
)
aggregate_failures "testing response" do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['description']).to eq('changed description')
expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\
'-/system/project/avatar/'\
"#{project_with_avatar.id}/banana_sample.gif")
end
end
it 'removes avatar from project with an avatar' do
put api("/projects/#{project_with_avatar.id}", user), params: { avatar: '' }
aggregate_failures "testing response" do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['avatar_url']).to be_nil
expect(project_with_avatar.reload.avatar_url).to be_nil
end
end
end end
it 'updates auto_devops_deploy_strategy' do it 'updates auto_devops_deploy_strategy' do

View File

@ -51,7 +51,7 @@ const (
gitProjectPattern = `^/.+\.git/` gitProjectPattern = `^/.+\.git/`
geoGitProjectPattern = `^/[^-].+\.git/` // Prevent matching routes like /-/push_from_secondary geoGitProjectPattern = `^/[^-].+\.git/` // Prevent matching routes like /-/push_from_secondary
projectPattern = `^/([^/]+/){1,}[^/]+/` projectPattern = `^/([^/]+/){1,}[^/]+/`
apiProjectPattern = apiPattern + `v4/projects/[^/]+/` // API: Projects can be encoded via group%2Fsubgroup%2Fproject apiProjectPattern = apiPattern + `v4/projects/[^/]+` // API: Projects can be encoded via group%2Fsubgroup%2Fproject
apiTopicPattern = apiPattern + `v4/topics` apiTopicPattern = apiPattern + `v4/topics`
snippetUploadPattern = `^/uploads/personal_snippet` snippetUploadPattern = `^/uploads/personal_snippet`
userUploadPattern = `^/uploads/user` userUploadPattern = `^/uploads/user`
@ -269,40 +269,40 @@ func configureRoutes(u *upstream) {
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56731. // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56731.
// Maven Artifact Repository // Maven Artifact Repository
u.route("PUT", apiProjectPattern+`packages/maven/`, requestBodyUploader), u.route("PUT", apiProjectPattern+`/packages/maven/`, requestBodyUploader),
// Conan Artifact Repository // Conan Artifact Repository
u.route("PUT", apiPattern+`v4/packages/conan/`, requestBodyUploader), u.route("PUT", apiPattern+`v4/packages/conan/`, requestBodyUploader),
u.route("PUT", apiProjectPattern+`packages/conan/`, requestBodyUploader), u.route("PUT", apiProjectPattern+`/packages/conan/`, requestBodyUploader),
// Generic Packages Repository // Generic Packages Repository
u.route("PUT", apiProjectPattern+`packages/generic/`, requestBodyUploader), u.route("PUT", apiProjectPattern+`/packages/generic/`, requestBodyUploader),
// NuGet Artifact Repository // NuGet Artifact Repository
u.route("PUT", apiProjectPattern+`packages/nuget/`, mimeMultipartUploader), u.route("PUT", apiProjectPattern+`/packages/nuget/`, mimeMultipartUploader),
// PyPI Artifact Repository // PyPI Artifact Repository
u.route("POST", apiProjectPattern+`packages/pypi`, mimeMultipartUploader), u.route("POST", apiProjectPattern+`/packages/pypi`, mimeMultipartUploader),
// Debian Artifact Repository // Debian Artifact Repository
u.route("PUT", apiProjectPattern+`packages/debian/`, requestBodyUploader), u.route("PUT", apiProjectPattern+`/packages/debian/`, requestBodyUploader),
// RPM Artifact Repository // RPM Artifact Repository
u.route("POST", apiProjectPattern+`packages/rpm/`, requestBodyUploader), u.route("POST", apiProjectPattern+`packages/rpm/`, requestBodyUploader),
// Gem Artifact Repository // Gem Artifact Repository
u.route("POST", apiProjectPattern+`packages/rubygems/`, requestBodyUploader), u.route("POST", apiProjectPattern+`/packages/rubygems/`, requestBodyUploader),
// Terraform Module Package Repository // Terraform Module Package Repository
u.route("PUT", apiProjectPattern+`packages/terraform/modules/`, requestBodyUploader), u.route("PUT", apiProjectPattern+`/packages/terraform/modules/`, requestBodyUploader),
// Helm Artifact Repository // Helm Artifact Repository
u.route("POST", apiProjectPattern+`packages/helm/api/[^/]+/charts\z`, mimeMultipartUploader), u.route("POST", apiProjectPattern+`/packages/helm/api/[^/]+/charts\z`, mimeMultipartUploader),
// We are porting API to disk acceleration // We are porting API to disk acceleration
// we need to declare each routes until we have fixed all the routes on the rails codebase. // we need to declare each routes until we have fixed all the routes on the rails codebase.
// Overall status can be seen at https://gitlab.com/groups/gitlab-org/-/epics/1802#current-status // Overall status can be seen at https://gitlab.com/groups/gitlab-org/-/epics/1802#current-status
u.route("POST", apiProjectPattern+`wikis/attachments\z`, tempfileMultipartProxy), u.route("POST", apiProjectPattern+`/wikis/attachments\z`, tempfileMultipartProxy),
u.route("POST", apiPattern+`graphql\z`, tempfileMultipartProxy), u.route("POST", apiPattern+`graphql\z`, tempfileMultipartProxy),
u.route("POST", apiTopicPattern, tempfileMultipartProxy), u.route("POST", apiTopicPattern, tempfileMultipartProxy),
u.route("PUT", apiTopicPattern, tempfileMultipartProxy), u.route("PUT", apiTopicPattern, tempfileMultipartProxy),
@ -315,16 +315,20 @@ func configureRoutes(u *upstream) {
u.route("POST", importPattern+`gitlab_group`, mimeMultipartUploader), u.route("POST", importPattern+`gitlab_group`, mimeMultipartUploader),
// Issuable Metric image upload // Issuable Metric image upload
u.route("POST", apiProjectPattern+`issues/[0-9]+/metric_images\z`, mimeMultipartUploader), u.route("POST", apiProjectPattern+`/issues/[0-9]+/metric_images\z`, mimeMultipartUploader),
// Alert Metric image upload // Alert Metric image upload
u.route("POST", apiProjectPattern+`alert_management_alerts/[0-9]+/metric_images\z`, mimeMultipartUploader), u.route("POST", apiProjectPattern+`/alert_management_alerts/[0-9]+/metric_images\z`, mimeMultipartUploader),
// Requirements Import via UI upload acceleration // Requirements Import via UI upload acceleration
u.route("POST", projectPattern+`requirements_management/requirements/import_csv`, mimeMultipartUploader), u.route("POST", projectPattern+`requirements_management/requirements/import_csv`, mimeMultipartUploader),
// Uploads via API // Uploads via API
u.route("POST", apiProjectPattern+`uploads\z`, mimeMultipartUploader), u.route("POST", apiProjectPattern+`/uploads\z`, mimeMultipartUploader),
// Project Avatar
u.route("POST", apiPattern+`v4/projects\z`, tempfileMultipartProxy),
u.route("PUT", apiProjectPattern+`\z`, tempfileMultipartProxy),
// Explicitly proxy API requests // Explicitly proxy API requests
u.route("", apiPattern, proxy), u.route("", apiPattern, proxy),

View File

@ -122,6 +122,10 @@ func TestAcceleratedUpload(t *testing.T) {
{"POST", `/example`, false}, {"POST", `/example`, false},
{"POST", `/uploads/personal_snippet`, true}, {"POST", `/uploads/personal_snippet`, true},
{"POST", `/uploads/user`, true}, {"POST", `/uploads/user`, true},
{"POST", `/api/v4/projects`, false},
{"PUT", `/api/v4/projects/group%2Fproject`, false},
{"PUT", `/api/v4/projects/group%2Fsubgroup%2Fproject`, false},
{"PUT", `/api/v4/projects/39`, false},
{"POST", `/api/v4/projects/1/uploads`, true}, {"POST", `/api/v4/projects/1/uploads`, true},
{"POST", `/api/v4/projects/group%2Fproject/uploads`, true}, {"POST", `/api/v4/projects/group%2Fproject/uploads`, true},
{"POST", `/api/v4/projects/group%2Fsubgroup%2Fproject/uploads`, true}, {"POST", `/api/v4/projects/group%2Fsubgroup%2Fproject/uploads`, true},