Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-08-16 06:10:05 +00:00
parent 473d6b2434
commit 99f7d9e732
24 changed files with 311 additions and 178 deletions

View File

@ -2,36 +2,6 @@
# Cop supports --auto-correct.
Layout/HashAlignment:
Exclude:
- 'ee/app/graphql/types/vulnerabilities/asset_type.rb'
- 'ee/app/graphql/types/vulnerabilities/container_image_type.rb'
- 'ee/app/graphql/types/vulnerabilities/link_type.rb'
- 'ee/app/graphql/types/vulnerability/external_issue_link_type.rb'
- 'ee/app/graphql/types/vulnerability/issue_link_type.rb'
- 'ee/app/graphql/types/vulnerability_details/base_type.rb'
- 'ee/app/graphql/types/vulnerability_details/boolean_type.rb'
- 'ee/app/graphql/types/vulnerability_details/code_type.rb'
- 'ee/app/graphql/types/vulnerability_details/commit_type.rb'
- 'ee/app/graphql/types/vulnerability_details/diff_type.rb'
- 'ee/app/graphql/types/vulnerability_details/file_location_type.rb'
- 'ee/app/graphql/types/vulnerability_details/int_type.rb'
- 'ee/app/graphql/types/vulnerability_details/list_type.rb'
- 'ee/app/graphql/types/vulnerability_details/markdown_type.rb'
- 'ee/app/graphql/types/vulnerability_details/module_location_type.rb'
- 'ee/app/graphql/types/vulnerability_details/table_type.rb'
- 'ee/app/graphql/types/vulnerability_details/text_type.rb'
- 'ee/app/graphql/types/vulnerability_details/url_type.rb'
- 'ee/app/graphql/types/vulnerability_location/container_scanning_type.rb'
- 'ee/app/graphql/types/vulnerability_location/coverage_fuzzing_type.rb'
- 'ee/app/graphql/types/vulnerability_location/dast_type.rb'
- 'ee/app/graphql/types/vulnerability_location/dependency_scanning_type.rb'
- 'ee/app/graphql/types/vulnerability_location/generic_type.rb'
- 'ee/app/graphql/types/vulnerability_location/sast_type.rb'
- 'ee/app/graphql/types/vulnerability_location/secret_detection_type.rb'
- 'ee/app/graphql/types/vulnerability_request_response_header_type.rb'
- 'ee/app/graphql/types/vulnerability_request_type.rb'
- 'ee/app/graphql/types/vulnerability_response_type.rb'
- 'ee/app/graphql/types/vulnerability_scanner_type.rb'
- 'ee/app/graphql/types/vulnerability_type.rb'
- 'ee/app/graphql/types/vulnerable_dependency_type.rb'
- 'ee/app/graphql/types/vulnerable_kubernetes_resource_type.rb'
- 'ee/app/graphql/types/vulnerable_projects_by_grade_type.rb'

View File

@ -38,10 +38,8 @@ export function formatIssue(issue) {
export function formatListIssues(listIssues) {
const boardItems = {};
let listItemsCount;
const listData = listIssues.nodes.reduce((map, list) => {
listItemsCount = list.issuesCount;
let sortedIssues = list.issues.edges.map((issueNode) => ({
...issueNode.node,
}));
@ -67,7 +65,7 @@ export function formatListIssues(listIssues) {
};
}, {});
return { listData, boardItems, listItemsCount };
return { listData, boardItems };
}
export function formatListsPageInfo(lists) {

View File

@ -117,7 +117,7 @@ export default {
return 'issues';
},
itemsTooltipLabel() {
return n__(`%d issue`, `%d issues`, this.boardLists?.issuesCount);
return n__(`%d issue`, `%d issues`, this.boardList?.issuesCount);
},
chevronTooltip() {
return this.list.collapsed ? this.$options.i18n.expand : this.$options.i18n.collapse;

View File

@ -17,7 +17,6 @@ query BoardListsEE(
lists(id: $id, issueFilters: $filters) {
nodes {
id
issuesCount
listType
issues(first: $first, filters: $filters, after: $after) {
edges {
@ -41,7 +40,6 @@ query BoardListsEE(
lists(id: $id, issueFilters: $filters) {
nodes {
id
issuesCount
listType
issues(first: $first, filters: $filters, after: $after) {
edges {

View File

@ -11,7 +11,7 @@ const updateListItemsCount = ({ state, listId, value }) => {
if (state.issuableType === issuableTypes.epic) {
Vue.set(state.boardLists, listId, { ...list, epicsCount: list.epicsCount + value });
} else {
Vue.set(state.boardLists, listId, { ...list, issuesCount: list.issuesCount + value });
Vue.set(state.boardLists, listId, { ...list });
}
};

View File

@ -2,10 +2,10 @@
import { GlAlert, GlFormGroup, GlForm, GlFormCombobox, GlButton, GlFormInput } from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { __, s__ } from '~/locale';
import projectWorkItemsQuery from '../../graphql/project_work_items.query.graphql';
import projectWorkItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql';
import updateWorkItemMutation from '../../graphql/update_work_item.mutation.graphql';
import createWorkItemMutation from '../../graphql/create_work_item.mutation.graphql';
import { WORK_ITEM_TYPE_IDS } from '../../constants';
import { TASK_TYPE_NAME } from '../../constants';
export default {
components: {
@ -35,23 +35,15 @@ export default {
},
},
apollo: {
availableWorkItems: {
query: projectWorkItemsQuery,
debounce: 200,
workItemTypes: {
query: projectWorkItemTypesQuery,
variables() {
return {
projectPath: this.projectPath,
searchTerm: this.search?.title || this.search,
types: ['TASK'],
fullPath: this.projectPath,
};
},
skip() {
return this.search.length === 0;
},
update(data) {
return data.workspace.workItems.edges
.filter((wi) => !this.childrenIds.includes(wi.node.id))
.map((wi) => wi.node);
return data.workspace?.workItemTypes?.nodes;
},
},
},
@ -82,6 +74,9 @@ export default {
addOrCreateMethod() {
return this.childToCreateTitle ? this.createChild : this.addChild;
},
taskWorkItemType() {
return this.workItemTypes.find((type) => type.name === TASK_TYPE_NAME)?.id;
},
},
methods: {
getIdFromGraphQLId,
@ -124,7 +119,7 @@ export default {
input: {
title: this.search?.title || this.search,
projectPath: this.projectPath,
workItemTypeId: WORK_ITEM_TYPE_IDS.TASK,
workItemTypeId: this.taskWorkItemType,
hierarchyWidget: {
parentId: this.issuableGid,
},

View File

@ -37,10 +37,6 @@ export const WORK_ITEM_STATUS_TEXT = {
OPEN: s__('WorkItem|Open'),
};
export const WORK_ITEM_TYPE_IDS = {
TASK: 'gid://gitlab/WorkItems::Type/5',
};
export const WORK_ITEMS_TYPE_MAP = {
[WORK_ITEM_TYPE_ENUM_INCIDENT]: {
icon: `issue-type-incident`,

View File

@ -14,11 +14,22 @@ module Projects
def show
end
def cleanup_tags
registry_settings_enabled!
@hide_search_settings = true
end
private
def packages_and_registries_settings_enabled!
render_404 unless can?(current_user, :view_package_registry_project_settings, project)
end
def registry_settings_enabled!
render_404 unless Gitlab.config.registry.enabled &&
can?(current_user, :admin_container_image, project)
end
end
end
end

View File

@ -63,4 +63,27 @@ module PackagesHelper
Gitlab.config.packages.enabled &&
Ability.allowed?(current_user, :admin_package, project)
end
def cleanup_settings_data
{
project_id: @project.id,
project_path: @project.full_path,
cadence_options: cadence_options.to_json,
keep_n_options: keep_n_options.to_json,
older_than_options: older_than_options.to_json,
is_admin: current_user&.admin.to_s,
admin_settings_path: ci_cd_admin_application_settings_path(anchor: 'js-registry-settings'),
enable_historic_entries: container_expiration_policies_historic_entry_enabled?.to_s,
help_page_path: help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'cleanup-policy'),
show_cleanup_policy_link: show_cleanup_policy_link(@project).to_s,
tags_regex_help_page_path: help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'regex-pattern-examples')
}
end
def settings_data
cleanup_settings_data.merge(
show_container_registry_settings: show_container_registry_settings(@project).to_s,
show_package_registry_settings: show_package_registry_settings(@project).to_s
)
end
end

View File

@ -0,0 +1,6 @@
- add_to_breadcrumbs _('Packages & Registries'), project_settings_packages_and_registries_path(@project)
- breadcrumb_title s_('ContainerRegistry|Clean up image tags')
- page_title s_('ContainerRegistry|Clean up image tags'), _('Packages & Registries')
- @content_class = 'limit-container-width' unless fluid_layout
#js-registry-settings-cleanup-image-tags{ data: cleanup_settings_data }

View File

@ -2,16 +2,4 @@
- page_title _('Packages & Registries')
- @content_class = 'limit-container-width' unless fluid_layout
#js-registry-settings{ data: { project_id: @project.id,
project_path: @project.full_path,
cadence_options: cadence_options.to_json,
keep_n_options: keep_n_options.to_json,
older_than_options: older_than_options.to_json,
is_admin: current_user&.admin.to_s,
show_container_registry_settings: show_container_registry_settings(@project).to_s,
show_package_registry_settings: show_package_registry_settings(@project).to_s,
admin_settings_path: ci_cd_admin_application_settings_path(anchor: 'js-registry-settings'),
enable_historic_entries: container_expiration_policies_historic_entry_enabled?.to_s,
help_page_path: help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'cleanup-policy'),
show_cleanup_policy_link: show_cleanup_policy_link(@project).to_s,
tags_regex_help_page_path: help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'regex-pattern-examples') } }
#js-registry-settings{ data: settings_data }

View File

@ -156,7 +156,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
resource :packages_and_registries, only: [:show]
resource :packages_and_registries, only: [:show] do
get '/cleanup_image_tags', to: 'packages_and_registries#cleanup_tags'
end
end
resources :usage_quotas, only: [:index]

View File

@ -813,26 +813,12 @@ information, see the [relevant documentation](monitoring.md#monitor-gitaly-concu
## Control groups
FLAG:
On self-managed GitLab, by default cgroups are not available. To make it available, ask an administrator to
On self-managed GitLab, by default repository cgroups are not available. To make it available, ask an administrator to
[enable the feature flag](../feature_flags.md) named `gitaly_run_cmds_in_cgroup`.
Gitaly shells out to Git for many of its operations. Git can consume a lot of resources for certain operations,
especially for large repositories.
Control groups (cgroups) in Linux allow limits to be imposed on how much memory and CPU can be consumed.
See the [`cgroups` Linux man page](https://man7.org/linux/man-pages/man7/cgroups.7.html) for more information.
cgroups can be useful for protecting the system against resource exhaustion because of overcomsumption of memory and CPU.
Gitaly has built-in cgroups control. When configured, Gitaly assigns Git
processes to a cgroup based on the repository the Git command is operating in.
Each cgroup has a memory and CPU limit. When a cgroup reaches its:
- Memory limit, the kernel looks through the processes for a candidate to kill.
- CPU limit, processes are not killed, but the processes are prevented from consuming more CPU than allowed.
The main reason to configure cgroups for your GitLab installation is that it
protects against system resource starvation due to a few large repositories or
bad actors.
cgroups can be useful for protecting the system against resource exhaustion because of over consumption of memory and CPU.
Some Git operations are expensive by nature. `git clone`, for instance,
spawns a `git-upload-pack` process on the server that can consume a lot of memory
@ -840,33 +826,33 @@ for large repositories. For example, a client that keeps on cloning a
large repository over and over again. This situation could potentially use up all of the
memory on a server, causing other operations to fail for other users.
There are many ways someone can create a repository that can consume large amounts of memory when cloned or downloaded.
A repository can consume large amounts of memory for many reasons when cloned or downloaded.
Using cgroups allows the kernel to kill these operations before they hog up all system resources.
### Configure cgroups in Gitaly
Gitaly shells out to Git for many of its operations. Git can consume a lot of resources for certain operations,
especially for large repositories.
Two ways of configuring cgroups are available.
Gitaly has built-in cgroups control. When configured, Gitaly assigns Git processes to a cgroup based on the repository
the Git command is operating in. These cgroups are called repository cgroups. Each repository cgroup:
#### Configure cgroups (new method)
- Has a memory and CPU limit.
- Contains the Git processes for a single repository.
- Uses a consistent hash to ensure a Git process for a given repository always ends up in the same cgroup.
> This method of configuring cgroups introduced in GitLab 15.1.
When a repository cgroup reaches its:
Gitaly creates a pool of cgroups that are isolated based on the repository used in the Git command to be placed under one of these cgroups.
- Memory limit, the kernel looks through the processes for a candidate to kill.
- CPU limit, processes are not killed, but the processes are prevented from consuming more CPU than allowed.
To configure cgroups in Gitaly, add `gitaly['cgroups']` to `/etc/gitlab/gitlab.rb`.
You configure repository cgroups for your GitLab installation to protect against system resource starvation from a few
large repositories or bad actors.
For example:
### Configure repository cgroups (new method)
```ruby
# in /etc/gitlab/gitlab.rb
gitaly['cgroups_mountpoint'] = "/sys/fs/cgroup"
gitaly['cgroups_hierarchy_root'] =>"gitaly"
gitaly['cgroups_memory_bytes'] = 64424509440, # 60gb
gitaly['cgroups_cpu_shares'] = 1024
gitaly['cgroups_repositories_count'] => 1000,
gitaly['cgroups_repositories_memory_bytes'] => 32212254720 # 20gb
gitaly['cgroups_repositories_cpu_shares'] => 512
```
> This method of configuring repository cgroups was introduced in GitLab 15.1.
To configure repository cgroups in Gitaly using the new method, use the following settings for the new configuration method
to `gitaly['cgroups']` in `/etc/gitlab/gitlab.rb`:
- `cgroups_mountpoint` is where the parent cgroup directory is mounted. Defaults to `/sys/fs/cgroup`.
- `cgroups_hierarchy_root` is the parent cgroup under which Gitaly creates groups, and
@ -875,7 +861,7 @@ gitaly['cgroups_repositories_cpu_shares'] => 512
when Gitaly starts.
- `cgroups_memory_bytes` is the total memory limit that is imposed collectively on all
Git processes that Gitaly spawns. 0 implies no limit.
- `cgroups_cpu_shares` is the cpu limit that is imposed collectively on all Git
- `cgroups_cpu_shares` is the CPU limit that is imposed collectively on all Git
processes that Gitaly spawns. 0 implies no limit. The maximum is 1024 shares,
which represents 100% of CPU.
- `cgroups_repositories_count` is the number of cgroups in the cgroups pool. Each time a new Git
@ -884,31 +870,29 @@ gitaly['cgroups_repositories_cpu_shares'] => 512
Git commands to these cgroups, so a Git command for a repository is
always assigned to the same cgroup.
- `cgroups_repositories_memory_bytes` is the total memory limit imposed on all Git processes contained in a repository cgroup.
A repository cgroup is one that contains Git processes for one or more repositories.
A consistent hash is used to assign repositories to these cgroups such that a Git process for a given repository always ends up in the same cgroup.
0 implies no limit. This value cannot exceed that of the top level `cgroups_memory_bytes`.
- `cgroups_repositories_cpu_shares` is the CPU limit that is imposed on all Git processes contained in a repository cgroup.
A repository cgroup is one that contains Git processes for one or more repositories.
A consistent hash is used to assign repositories to these cgroups such that a Git process for a given repository always ends up in the same cgroup.
0 implies no limit. The maximum is 1024 shares, which represents 100% of CPU.
This value cannot exceed that of the top level`cgroups_cpu_shares`.
#### Configure cgroups (legacy method)
To configure cgroups in Gitaly for GitLab versions using the legacy method, add `gitaly['cgroups']` to `/etc/gitlab/gitlab.rb`. For
example:
For example:
```ruby
# in /etc/gitlab/gitlab.rb
gitaly['cgroups_count'] = 1000
gitaly['cgroups_mountpoint'] = "/sys/fs/cgroup"
gitaly['cgroups_hierarchy_root'] = "gitaly"
gitaly['cgroups_memory_limit'] = 32212254720
gitaly['cgroups_memory_enabled'] = true
gitaly['cgroups_hierarchy_root'] => "gitaly"
gitaly['cgroups_memory_bytes'] = 64424509440, # 60gb
gitaly['cgroups_cpu_shares'] = 1024
gitaly['cgroups_cpu_enabled'] = true
gitaly['cgroups_repositories_count'] => 1000,
gitaly['cgroups_repositories_memory_bytes'] => 32212254720 # 20gb
gitaly['cgroups_repositories_cpu_shares'] => 512
```
### Configure repository cgroups (legacy method)
To configure repository cgroups in Gitaly using the legacy method, use the following settings
in `/etc/gitlab/gitlab.rb`:
- `cgroups_count` is the number of cgroups created. Each time a new
command is spawned, Gitaly assigns it to one of these cgroups based
on the command line arguments of the command. A circular hashing algorithm assigns
@ -921,7 +905,21 @@ gitaly['cgroups_cpu_enabled'] = true
- `cgroups_memory_enabled` enables or disables the memory limit on cgroups.
- `cgroups_memory_bytes` is the total memory limit each cgroup imposes on the processes added to it.
- `cgroups_cpu_enabled` enables or disables the CPU limit on cgroups.
- `cgroups_cpu_shares` is the CPU limit each cgroup imposes on the processes added to it. The maximum is 1024 shares, which represents 100% of CPU.
- `cgroups_cpu_shares` is the CPU limit each cgroup imposes on the processes added to it. The maximum is 1024 shares,
which represents 100% of CPU.
For example:
```ruby
# in /etc/gitlab/gitlab.rb
gitaly['cgroups_count'] = 1000
gitaly['cgroups_mountpoint'] = "/sys/fs/cgroup"
gitaly['cgroups_hierarchy_root'] = "gitaly"
gitaly['cgroups_memory_limit'] = 32212254720
gitaly['cgroups_memory_enabled'] = true
gitaly['cgroups_cpu_shares'] = 1024
gitaly['cgroups_cpu_enabled'] = true
```
### Configuring oversubscription
@ -930,16 +928,15 @@ In the previous example using the new configuration method:
- The top level memory limit is capped at 60gb.
- Each of the 1000 cgroups in the repositories pool is capped at 20gb.
This is called "oversubscription". Each cgroup in the pool has a much larger capacity than 1/1000th
This configuration leads to "oversubscription". Each cgroup in the pool has a much larger capacity than 1/1000th
of the top-level memory limit.
This strategy has two main benefits:
- It gives the host protection from overall memory starvation (OOM), because the top-level
cgroup's memory limit can be set to a threshold smaller than the host's
capacity. Processes outside of that cgroup are not at risk of OOM.
- It gives the host protection from overall memory starvation (OOM), because the memory limit of the top-level cgroup
can be set to a threshold smaller than the host's capacity. Processes outside of that cgroup are not at risk of OOM.
- It allows each individual cgroup in the pool to burst up to a generous upper
bound (in this example 20 GB) that is smaller than the parent cgroup's limit,
bound (in this example 20 GB) that is smaller than the limit of the parent cgroup,
but substantially larger than 1/N of the parent's limit. In this example, up
to 3 child cgroups can concurrently burst up to their max. In general, all
1000 cgroups would use much less than the 20 GB.

View File

@ -80,11 +80,10 @@ Gitaly Cluster does not support snapshot backups. Snapshot backups can cause iss
out of sync with the disk storage. Because of how Praefect rebuilds the replication metadata of Gitaly disk information
during a restore, we recommend using the [official backup and restore Rake tasks](../../raketasks/backup_restore.md).
If you are unable to use this method, please contact customer support for restoration help.
The [incremental backup method](../../raketasks/backup_gitlab.md#incremental-repository-backups)
can be used to speed up Gitaly Cluster backups.
We are tracking in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/351383) improvements to the
[official backup and restore Rake tasks](../../raketasks/backup_restore.md) to add support for incremental backups. For
more information, see [this epic](https://gitlab.com/groups/gitlab-org/-/epics/2094).
If you are unable to use either method, please contact customer support for restoration help.
### What to do if you are on Gitaly Cluster experiencing an issue or limitation

View File

@ -170,7 +170,7 @@ Each feature flag is defined in a separate YAML file consisting of a number of f
| `default_enabled` | yes | The default state of the feature flag. |
| `introduced_by_url` | no | The URL to the merge request that introduced the feature flag. |
| `rollout_issue_url` | no | The URL to the Issue covering the feature flag rollout. |
| `milestone` | no | Milestone in which the feature was added. |
| `milestone` | no | Milestone in which the feature flag was created. |
| `group` | no | The [group](https://about.gitlab.com/handbook/product/categories/#devops-stages) that owns the feature flag. |
NOTE:

View File

@ -403,11 +403,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
msgid "%d unassigned issue"
msgid_plural "%d unassigned issues"
msgstr[0] ""
msgstr[1] ""
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""

View File

@ -248,7 +248,7 @@
"webpack-dev-server": "4.10.0",
"xhr-mock": "^2.5.1",
"yarn-check-webpack-plugin": "^1.2.0",
"yarn-deduplicate": "^5.0.0"
"yarn-deduplicate": "^5.0.2"
},
"blockedDependencies": {
"bootstrap-vue": "https://docs.gitlab.com/ee/development/fe_guide/dependencies.html#bootstrapvue"

View File

@ -8,9 +8,14 @@ module QA
describe 'Project import' do
let(:logger) { Runtime::Logger.logger }
let(:differ) { RSpec::Support::Differ.new(color: true) }
let(:gitlab_address) { QA::Runtime::Scenario.gitlab_address }
let(:dummy_url) { "https://example.com" }
let(:created_by_pattern) { /\*Created by: \S+\*\n\n/ }
let(:suggestion_pattern) { /suggestion:-\d+\+\d+/ }
let(:gh_link_pattern) { %r{https://github.com/#{github_repo}/(issues|pull)} }
let(:gl_link_pattern) { %r{#{gitlab_address}/#{imported_project.path_with_namespace}/-/(issues|merge_requests)} }
let(:event_pattern) { %r{(un)?assigned( to)? @\S+|mentioned in (issue|merge request) [!#]\d+|changed title from \*\*.*\*\* to \*\*.*\*\*} } # rubocop:disable Layout/LineLength
let(:api_client) { Runtime::API::Client.as_admin }
@ -83,14 +88,14 @@ module QA
let(:gh_issue_comments) do
logger.debug("= Fetching issue comments =")
github_client.issues_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash|
hash[c.html_url.gsub(/\#\S+/, "")] << c.body # use base html url as key
hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url) # use base html url as key
end
end
let(:gh_pr_comments) do
logger.debug("= Fetching pr comments =")
github_client.pull_requests_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash|
hash[c.html_url.gsub(/\#\S+/, "")] << c.body # use base html url as key
hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url) # use base html url as key
end
end
@ -135,7 +140,7 @@ module QA
target: {
name: "GitLab",
project_name: imported_project.path_with_namespace,
address: QA::Runtime::Scenario.gitlab_address,
address: gitlab_address,
data: {
branches: gl_branches.length,
commits: gl_commits.length,
@ -381,15 +386,16 @@ module QA
end
logger.debug("Fetching comments for mr '#{mr[:title]}'")
comments = resource
.comments(auto_paginate: true, attempts: 2)
.reject { |c| c[:system] || c[:body].match?(/^(\*\*Review:\*\*)|(\*Merged by:).*/) }
[mr[:iid], {
url: mr[:web_url],
title: mr[:title],
body: sanitize_description(mr[:description]) || '',
comments: resource
.comments(auto_paginate: true, attempts: 2)
# remove system notes
.reject { |c| c[:system] || c[:body].match?(/^(\*\*Review:\*\*)|(\*Merged by:).*/) }
.map { |c| sanitize_comment(c[:body]) }
events: events(comments),
comments: non_event_comments(comments)
}]
end.to_h
end
@ -412,24 +418,54 @@ module QA
end
logger.debug("Fetching comments for issue '#{issue[:title]}'")
comments = resource.comments(auto_paginate: true, attempts: 2)
[issue[:iid], {
url: issue[:web_url],
title: issue[:title],
body: sanitize_description(issue[:description]) || '',
comments: resource
.comments(auto_paginate: true, attempts: 2)
.map { |c| sanitize_comment(c[:body]) }
events: events(comments),
comments: non_event_comments(comments)
}]
end.to_h
end
end
# Remove added prefixes and legacy diff format from comments
# Fetch comments without events
#
# @param [Array] comments
# @return [Array]
def non_event_comments(comments)
comments
.reject { |c| c[:body].match?(event_pattern) }
.map { |c| sanitize_comment(c[:body]) }
end
# Events
#
# @param [Array] comments
# @return [Array]
def events(comments)
comments
.select { |c| c[:body].match?(event_pattern) }
.map { |c| c[:body] }
end
# Normalize comments and make them directly comparable
#
# * remove created by prefixes
# * unify suggestion format
# * replace github and gitlab urls - some of the links to objects get transformed to gitlab entities, some don't,
# update all links to example.com for now
#
# @param [String] body
# @return [String]
def sanitize_comment(body)
body.gsub(created_by_pattern, "").gsub(suggestion_pattern, "suggestion\r")
body
.gsub(created_by_pattern, "")
.gsub(suggestion_pattern, "suggestion\r")
.gsub(gl_link_pattern, dummy_url)
.gsub(gh_link_pattern, dummy_url)
end
# Remove created by prefix from descripion

View File

@ -0,0 +1,49 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Project > Settings > Packages & Registries > Container registry tag expiration policy' do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) }
let(:container_registry_enabled) { true }
let(:container_registry_enabled_on_project) { ProjectFeature::ENABLED }
subject { visit cleanup_image_tags_project_settings_packages_and_registries_path(project) }
before do
project.project_feature.update!(container_registry_access_level: container_registry_enabled_on_project)
project.container_expiration_policy.update!(enabled: true)
sign_in(user)
stub_container_registry_config(enabled: container_registry_enabled)
end
context 'as owner', :js do
it 'shows available section' do
subject
expect(find('.breadcrumbs')).to have_content('Clean up image tags')
end
end
context 'when registry is disabled' do
let(:container_registry_enabled) { false }
it 'does not exists' do
subject
expect(page).to have_gitlab_http_status(:not_found)
end
end
context 'when container registry is disabled on project' do
let(:container_registry_enabled_on_project) { ProjectFeature::DISABLED }
it 'does not exists' do
subject
expect(page).to have_gitlab_http_status(:not_found)
end
end
end

View File

@ -518,17 +518,6 @@ describe('Board Store Mutations', () => {
expect(state.boardItemsByListId[payload.listId]).toEqual(listState);
});
it("updates the list's items count", () => {
expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(1);
mutations.ADD_BOARD_ITEM_TO_LIST(state, {
itemId: mockIssue2.id,
listId: mockList.id,
});
expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(2);
});
});
describe('REMOVE_BOARD_ITEM_FROM_LIST', () => {
@ -536,8 +525,7 @@ describe('Board Store Mutations', () => {
setBoardsListsState();
});
it("removes an item from a list and updates the list's items count", () => {
expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(1);
it('removes an item from a list', () => {
expect(state.boardItemsByListId['gid://gitlab/List/1']).toContain(mockIssue.id);
mutations.REMOVE_BOARD_ITEM_FROM_LIST(state, {
@ -546,7 +534,6 @@ describe('Board Store Mutations', () => {
});
expect(state.boardItemsByListId['gid://gitlab/List/1']).not.toContain(mockIssue.id);
expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(0);
});
});

View File

@ -5,12 +5,13 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import WorkItemLinksForm from '~/work_items/components/work_item_links/work_item_links_form.vue';
import { WORK_ITEM_TYPE_IDS } from '~/work_items/constants';
import projectWorkItemsQuery from '~/work_items/graphql/project_work_items.query.graphql';
import projectWorkItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql';
import createWorkItemMutation from '~/work_items/graphql/create_work_item.mutation.graphql';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import {
availableWorkItemsResponse,
projectWorkItemTypesQueryResponse,
createWorkItemMutationResponse,
updateWorkItemMutationResponse,
} from '../../mock_data';
@ -25,11 +26,13 @@ describe('WorkItemLinksForm', () => {
const createComponent = async ({
listResponse = availableWorkItemsResponse,
typesResponse = projectWorkItemTypesQueryResponse,
parentConfidential = false,
} = {}) => {
wrapper = shallowMountExtended(WorkItemLinksForm, {
apolloProvider: createMockApollo([
[projectWorkItemsQuery, jest.fn().mockResolvedValue(listResponse)],
[projectWorkItemTypesQuery, jest.fn().mockResolvedValue(typesResponse)],
[updateWorkItemMutation, updateMutationResolver],
[createWorkItemMutation, createMutationResolver],
]),
@ -70,7 +73,7 @@ describe('WorkItemLinksForm', () => {
input: {
title: 'Create task test',
projectPath: 'project/path',
workItemTypeId: WORK_ITEM_TYPE_IDS.TASK,
workItemTypeId: 'gid://gitlab/WorkItems::Type/3',
hierarchyWidget: {
parentId: 'gid://gitlab/WorkItem/1',
},
@ -92,7 +95,7 @@ describe('WorkItemLinksForm', () => {
input: {
title: 'Create confidential task',
projectPath: 'project/path',
workItemTypeId: WORK_ITEM_TYPE_IDS.TASK,
workItemTypeId: 'gid://gitlab/WorkItems::Type/3',
hierarchyWidget: {
parentId: 'gid://gitlab/WorkItem/1',
},

View File

@ -0,0 +1,70 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Settings::PackagesAndRegistriesController do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) }
let(:container_registry_enabled) { true }
let(:container_registry_enabled_on_project) { ProjectFeature::ENABLED }
before do
project.project_feature.update!(container_registry_access_level: container_registry_enabled_on_project)
project.container_expiration_policy.update!(enabled: true)
stub_container_registry_config(enabled: container_registry_enabled)
end
describe 'GET #cleanup_tags' do
subject { get cleanup_image_tags_namespace_project_settings_packages_and_registries_path(user.namespace, project) }
context 'when user is unauthorized' do
let_it_be(:user) { create(:user) }
before do
project.add_reporter(user)
sign_in(user)
subject
end
it 'shows 404' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when user is authorized' do
let(:user) { project.creator }
before do
sign_in(user)
subject
end
it 'renders content' do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:cleanup_tags)
end
context 'when registry is disabled' do
let(:container_registry_enabled) { false }
it 'shows 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when container registry is disabled on project' do
let(:container_registry_enabled_on_project) { ProjectFeature::DISABLED }
it 'shows 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
end

View File

@ -772,6 +772,16 @@ RSpec.describe 'project routing' do
end
end
describe Projects::Settings::PackagesAndRegistriesController, 'routing' do
it 'to #show' do
expect(get('/gitlab/gitlabhq/-/settings/packages_and_registries')).to route_to('projects/settings/packages_and_registries#show', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
it 'to #cleanup_tags' do
expect(get('gitlab/gitlabhq/-/settings/packages_and_registries/cleanup_image_tags')).to route_to('projects/settings/packages_and_registries#cleanup_tags', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end
describe Projects::Settings::IntegrationsController, 'routing' do
it 'to #index' do
expect(get('/gitlab/gitlabhq/-/settings/integrations')).to route_to('projects/settings/integrations#index', namespace_id: 'gitlab', project_id: 'gitlabhq')

View File

@ -3549,10 +3549,10 @@ commander@^6.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
commander@^9.2.0:
version "9.2.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.2.0.tgz#6e21014b2ed90d8b7c9647230d8b7a94a4a419a9"
integrity sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==
commander@^9.4.0:
version "9.4.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.0.tgz#bc4a40918fefe52e22450c111ecd6b7acce6f11c"
integrity sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==
commander@~9.0.0:
version "9.0.0"
@ -10496,7 +10496,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6:
semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7:
version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
@ -11486,7 +11486,7 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@~2.4.0:
tslib@^2, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.4.0, tslib@~2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
@ -12459,15 +12459,15 @@ yarn-check-webpack-plugin@^1.2.0:
dependencies:
chalk "^2.4.2"
yarn-deduplicate@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-5.0.0.tgz#8977b9a4b1a2fd905568c3a23507b1021fa381eb"
integrity sha512-sYA5tqBSY3m+DtEcwfMYP1G2zWq1UtWSNg2goESqiu/JXBoBF/Qh+FuTJGGjsrisxL+5yOgq/ez1Rd+KSPwzvA==
yarn-deduplicate@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-5.0.2.tgz#b56484c94d8f1163a828bf20516607f89c078675"
integrity sha512-pxKa+dM7DMQ4X2vYLKqGCUgtEoTtdMVk9gNoIsxsMSP0rOV51IWFcKHfRIcZjAPNgHTrxz46sKB4xr7Nte7jdw==
dependencies:
"@yarnpkg/lockfile" "^1.1.0"
commander "^9.2.0"
semver "^7.3.2"
tslib "^2.3.1"
commander "^9.4.0"
semver "^7.3.7"
tslib "^2.4.0"
yn@3.1.1:
version "3.1.1"