Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e2d4a6dedb
commit
5e99b288a0
|
@ -1,16 +1,6 @@
|
|||
<script>
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import {
|
||||
GlAlert,
|
||||
GlIcon,
|
||||
GlLoadingIcon,
|
||||
GlNewDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlSprintf,
|
||||
GlTabs,
|
||||
GlTab,
|
||||
GlButton,
|
||||
} from '@gitlab/ui';
|
||||
import { GlAlert, GlIcon, GlLoadingIcon, GlSprintf, GlTabs, GlTab, GlButton } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import query from '../graphql/queries/details.query.graphql';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
|
@ -38,8 +28,6 @@ export default {
|
|||
GlAlert,
|
||||
GlIcon,
|
||||
GlLoadingIcon,
|
||||
GlNewDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlSprintf,
|
||||
GlTab,
|
||||
GlTabs,
|
||||
|
@ -148,15 +136,6 @@ export default {
|
|||
class="gl-display-flex gl-justify-content-space-between gl-align-items-center"
|
||||
>
|
||||
<h2 data-testid="title">{{ alert.title }}</h2>
|
||||
<gl-new-dropdown right>
|
||||
<gl-new-dropdown-item
|
||||
v-for="(label, field) in $options.statuses"
|
||||
:key="field"
|
||||
data-testid="statusDropdownItem"
|
||||
class="gl-vertical-align-middle"
|
||||
>{{ label }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-new-dropdown>
|
||||
</div>
|
||||
<gl-tabs v-if="alert" data-testid="alertDetailsTabs">
|
||||
<gl-tab data-testid="overviewTab" :title="$options.i18n.overviewTitle">
|
||||
|
|
|
@ -46,7 +46,7 @@ module Groups
|
|||
end
|
||||
|
||||
def deploy_token_params
|
||||
params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :username)
|
||||
params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :read_package_registry, :write_package_registry, :username)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -146,7 +146,7 @@ class GroupsController < Groups::ApplicationController
|
|||
export_service = Groups::ImportExport::ExportService.new(group: @group, user: current_user)
|
||||
|
||||
if export_service.async_execute
|
||||
redirect_to edit_group_path(@group), notice: _('Group export started.')
|
||||
redirect_to edit_group_path(@group), notice: _('Group export started. A download link will be sent by email.')
|
||||
else
|
||||
redirect_to edit_group_path(@group), alert: _('Group export could not be started.')
|
||||
end
|
||||
|
|
|
@ -89,7 +89,7 @@ module Projects
|
|||
end
|
||||
|
||||
def deploy_token_params
|
||||
params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :username)
|
||||
params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :read_package_registry, :write_package_registry, :username)
|
||||
end
|
||||
|
||||
def access_levels_options
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Emails
|
||||
module Groups
|
||||
def group_was_exported_email(current_user, group)
|
||||
group_email(current_user, group, _('Group was exported'))
|
||||
end
|
||||
|
||||
def group_was_not_exported_email(current_user, group, errors)
|
||||
group_email(current_user, group, _('Group export error'), errors: errors)
|
||||
end
|
||||
|
||||
def group_email(current_user, group, subj, errors: nil)
|
||||
@group = group
|
||||
@errors = errors
|
||||
mail(to: current_user.notification_email_for(@group), subject: subject(subj))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,6 +17,7 @@ class Notify < ApplicationMailer
|
|||
include Emails::AutoDevops
|
||||
include Emails::RemoteMirrors
|
||||
include Emails::Releases
|
||||
include Emails::Groups
|
||||
|
||||
helper MilestonesHelper
|
||||
helper MergeRequestsHelper
|
||||
|
|
|
@ -96,6 +96,8 @@ module Groups
|
|||
group_name: @group.name,
|
||||
message: 'Group Import/Export: Export succeeded'
|
||||
)
|
||||
|
||||
notification_service.group_was_exported(@group, @current_user)
|
||||
end
|
||||
|
||||
def notify_error
|
||||
|
@ -105,6 +107,12 @@ module Groups
|
|||
error: @shared.errors.join(', '),
|
||||
message: 'Group Import/Export: Export failed'
|
||||
)
|
||||
|
||||
notification_service.group_was_not_exported(@group, @current_user, @shared.errors)
|
||||
end
|
||||
|
||||
def notification_service
|
||||
@notification_service ||= NotificationService.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -545,6 +545,18 @@ class NotificationService
|
|||
end
|
||||
end
|
||||
|
||||
def group_was_exported(group, current_user)
|
||||
return true unless notifiable?(current_user, :mention, group: group)
|
||||
|
||||
mailer.group_was_exported_email(current_user, group).deliver_later
|
||||
end
|
||||
|
||||
def group_was_not_exported(group, current_user, errors)
|
||||
return true unless notifiable?(current_user, :mention, group: group)
|
||||
|
||||
mailer.group_was_not_exported_email(current_user, group, errors).deliver_later
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def new_resource_email(target, method)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
%li= _('Projects')
|
||||
%li= _('Runner tokens')
|
||||
%li= _('SAML discovery tokens')
|
||||
%p= _('Once the exported file is ready you can download it from this page.')
|
||||
%p= _('Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page.')
|
||||
- if group.export_file_exists?
|
||||
= link_to _('Regenerate export'), export_group_path(group),
|
||||
method: :post, class: 'btn btn-default', data: { qa_selector: 'regenerate_export_group_link' }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- breadcrumb_title _('Repository Settings')
|
||||
- page_title _('Repository')
|
||||
|
||||
- deploy_token_description = s_('DeployTokens|Group deploy tokens allow read-only access to the repositories and registry images within the group.')
|
||||
- deploy_token_description = s_('DeployTokens|Group deploy tokens allow access to the packages, repositories, and registry images within the group.')
|
||||
|
||||
= render "shared/deploy_tokens/index", group_or_project: @group, description: deploy_token_description
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
%p
|
||||
= _('Group %{group_name} was exported successfully.') % { group_name: @group.name }
|
||||
|
||||
%p
|
||||
= _('The group export can be downloaded from:')
|
||||
= link_to download_export_group_url(@group), rel: 'nofollow', download: '' do
|
||||
#{@group.full_name} export
|
||||
%p
|
||||
= _('The download link will expire in 24 hours.')
|
|
@ -0,0 +1,6 @@
|
|||
<%= _('Group %{group_name} was exported successfully.') % { group_name: @group.name } %>
|
||||
|
||||
<%= _('The group export can be downloaded from:') %>
|
||||
<%= download_export_group_url(@group) %>
|
||||
|
||||
<%= _('The download link will expire in 24 hours.') %>
|
|
@ -0,0 +1,10 @@
|
|||
%p
|
||||
= _("Group %{group_name} couldn't be exported.") % { group_name: @group.name }
|
||||
|
||||
%p
|
||||
= _('The errors we encountered were:')
|
||||
|
||||
%ul
|
||||
- @errors.each do |error|
|
||||
%li
|
||||
#{error}
|
|
@ -0,0 +1,7 @@
|
|||
<%= _("Group %{group_name} couldn't be exported.") % { group_name: @group.name } %>
|
||||
|
||||
<%= _('The errors we encountered were:') %>
|
||||
|
||||
<% @errors.each do |error| -%>
|
||||
- <%= error %>
|
||||
<% end -%>
|
|
@ -22,13 +22,13 @@
|
|||
.content-list.manage-labels-list.js-prioritized-labels{ data: { url: set_priorities_project_labels_path(@project), sortable: can_admin_label } }
|
||||
#js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty? && search.blank?}" }
|
||||
= render 'shared/empty_states/priority_labels'
|
||||
- if @prioritized_labels.present?
|
||||
- if @prioritized_labels.any?
|
||||
= render partial: 'shared/label', collection: @prioritized_labels, as: :label, locals: { force_priority: true, subject: @project }
|
||||
- elsif search.present?
|
||||
.nothing-here-block
|
||||
= _('No prioritized labels with such name or description')
|
||||
|
||||
- if @labels.present?
|
||||
- if @labels.any?
|
||||
.other-labels
|
||||
%h5{ class: ('hide' if hide) }= _('Other Labels')
|
||||
.content-list.manage-labels-list.js-other-labels
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- breadcrumb_title _("Repository Settings")
|
||||
- page_title _("Repository")
|
||||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
- deploy_token_description = s_('DeployTokens|Deploy tokens allow access to your repository and registry images.')
|
||||
- deploy_token_description = s_('DeployTokens|Deploy tokens allow access to packages, your repository, and registry images.')
|
||||
|
||||
= render "projects/default_branch/show"
|
||||
= render_if_exists "projects/push_rules/index"
|
||||
|
|
|
@ -35,5 +35,15 @@
|
|||
= label_tag ("deploy_token_write_registry"), 'write_registry', class: 'label-bold form-check-label'
|
||||
.text-secondary= s_('DeployTokens|Allows write access to the registry images')
|
||||
|
||||
%fieldset.form-group.form-check
|
||||
= f.check_box :read_package_registry, class: 'form-check-input'
|
||||
= label_tag ("deploy_token_read_package_registry"), 'read_package_registry', class: 'label-bold form-check-label'
|
||||
.text-secondary= s_('DeployTokens|Allows read access to the package registry')
|
||||
|
||||
%fieldset.form-group.form-check
|
||||
= f.check_box :write_package_registry, class: 'form-check-input'
|
||||
= label_tag ("deploy_token_write_package_registry"), 'write_package_registry', class: 'label-bold form-check-label'
|
||||
.text-secondary= s_('DeployTokens|Allows write access to the package registry')
|
||||
|
||||
.prepend-top-default
|
||||
= f.submit s_('DeployTokens|Create deploy token'), class: 'btn btn-success qa-create-deploy-token'
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add email notification on group export complete
|
||||
merge_request: 30522
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add read and write package registry scopes to deploy tokens
|
||||
merge_request: 31267
|
||||
author:
|
||||
type: added
|
|
@ -1,17 +1,26 @@
|
|||
# Praefect: High Availability
|
||||
---
|
||||
type: reference
|
||||
---
|
||||
|
||||
NOTE: **Note:** Praefect is a
|
||||
[beta](https://about.gitlab.com/handbook/product/#alpha-beta-ga) component that
|
||||
allows Gitaly to be run in a highly available configuration. While unexpected
|
||||
data loss is not likely, Praefect is not yet ready for production environments.
|
||||
# Gitaly Cluster
|
||||
|
||||
[Gitaly](index.md) is the service that provides storage for Git repositories in
|
||||
the GitLab application. Praefect is an optional reverse proxy for Gitaly to
|
||||
manage multiple Gitaly nodes for high availability.
|
||||
[Gitaly](index.md), the service that provides storage for Git repositories, can
|
||||
be run in a clustered configuration to increase fault tolerance. In this
|
||||
configuration, every Git repository is stored on every Gitaly node in the
|
||||
cluster. Multiple clusters (or shards), can be configured.
|
||||
|
||||
High availability is currently implemented through **asynchronous replication**.
|
||||
If a Gitaly node becomes unavailable, Praefect will automatically route traffic
|
||||
to a warm Gitaly replica.
|
||||
Praefect is a router and transaction manager for Gitaly, and a required
|
||||
component for running a Gitaly Cluster.
|
||||
|
||||

|
||||
|
||||
Using a Gitaly Cluster increase fault tolerance by:
|
||||
|
||||
- Replicating write operations to warm standby Gitaly nodes.
|
||||
- Detecting Gitaly node failures.
|
||||
- Automatically routing Git requests to an available Gitaly node.
|
||||
|
||||
The availability objectives for Gitaly clusters are:
|
||||
|
||||
- **Recovery Point Objective (RPO):** Less than 1 minute.
|
||||
|
||||
|
@ -35,22 +44,22 @@ The current version supports:
|
|||
- Eventual consistency of the secondary replicas.
|
||||
- Automatic failover from the primary to the secondary.
|
||||
- Reporting of possible data loss if replication queue is non empty.
|
||||
- Marking the newly promoted primary read only if possible data loss is
|
||||
detected.
|
||||
|
||||
Follow the [HA Gitaly epic](https://gitlab.com/groups/gitlab-org/-/epics/1489)
|
||||
for improvements including
|
||||
[horizontally distributing reads](https://gitlab.com/groups/gitlab-org/-/epics/2013).
|
||||
|
||||
## Requirements for configuring Gitaly for High Availability
|
||||
## Requirements for configuring a Gitaly Cluster
|
||||
|
||||
A minimum highly available configuration requires:
|
||||
The minimum recommended configuration for a Gitaly Cluster requires:
|
||||
|
||||
- 1 highly available load balancer
|
||||
- 1 highly available PostgreSQL server (PostgreSQL 9.6 or newer)
|
||||
- 3 Praefect nodes
|
||||
- 3 Gitaly nodes (1 primary, 2 secondary)
|
||||
|
||||

|
||||
|
||||
See the [design
|
||||
document](https://gitlab.com/gitlab-org/gitaly/-/blob/master/doc/design_ha.md)
|
||||
for implementation details.
|
||||
|
|
|
@ -4,12 +4,8 @@ type: reference
|
|||
|
||||
# Configuring Gitaly for Scaled and High Availability
|
||||
|
||||
Gitaly does not yet support full high availability. However, Gitaly is quite
|
||||
stable and is in use on GitLab.com. Scaled and highly available GitLab environments
|
||||
should consider using Gitaly on a separate node.
|
||||
|
||||
See the [Gitaly HA Epic](https://gitlab.com/groups/gitlab-org/-/epics/289) to
|
||||
track plans and progress toward high availability support.
|
||||
A [Gitaly Cluster](../gitaly/praefect.md) can be used to increase the fault
|
||||
tolerance of Gitaly in high availability configurations.
|
||||
|
||||
This document is relevant for [scalable and highly available setups](../reference_architectures/index.md).
|
||||
|
||||
|
|
|
@ -12,9 +12,6 @@ performance, especially for actions that read or write to Git repositories. See
|
|||
[Filesystem Performance Benchmarking](../operations/filesystem_benchmarking.md)
|
||||
for steps to test filesystem performance.
|
||||
|
||||
NOTE: **Note:** [Cloud Object Storage service](object_storage.md) with [Gitaly](gitaly.md)
|
||||
is recommended over NFS wherever possible for improved performance.
|
||||
|
||||
## NFS Server features
|
||||
|
||||
### Required features
|
||||
|
|
|
@ -92,7 +92,7 @@ POST /projects/:id/deploy_tokens
|
|||
| `name` | string | yes | New deploy token's name |
|
||||
| `expires_at` | datetime | no | Expiration date for the deploy token. Does not expire if no value is provided. |
|
||||
| `username` | string | no | Username for deploy token. Default is `gitlab+deploy-token-{n}` |
|
||||
| `scopes` | array of strings | yes | Indicates the deploy token scopes. Must be at least one of `read_repository`, `read_registry`, or `write_registry`. |
|
||||
| `scopes` | array of strings | yes | Indicates the deploy token scopes. Must be at least one of `read_repository`, `read_registry`, `write_registry`, `read_package_registry`, or `write_package_registry`. |
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"name": "My deploy token", "expires_at": "2021-01-01", "username": "custom-user", "scopes": ["read_repository"]}' "https://gitlab.example.com/api/v4/projects/5/deploy_tokens/"
|
||||
|
@ -193,7 +193,7 @@ POST /groups/:id/deploy_tokens
|
|||
| `name` | string | yes | New deploy token's name |
|
||||
| `expires_at` | datetime | no | Expiration date for the deploy token. Does not expire if no value is provided. |
|
||||
| `username` | string | no | Username for deploy token. Default is `gitlab+deploy-token-{n}` |
|
||||
| `scopes` | array of strings | yes | Indicates the deploy token scopes. Must be at least one of `read_repository`, `read_registry`, or `write_registry`. |
|
||||
| `scopes` | array of strings | yes | Indicates the deploy token scopes. Must be at least one of `read_repository`, `read_registry`, `write_registry`, `read_package_registry`, or `write_package_registry`. |
|
||||
|
||||
Example request:
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ is a performance optimization that "allows Git to function without having a
|
|||
complete copy of the repository. The goal of this work is to allow Git better
|
||||
handle extremely large repositories."
|
||||
|
||||
Git 2.22.0 or later is required.
|
||||
|
||||
## Filter by file size
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/2553) in GitLab 12.10.
|
||||
|
@ -118,7 +120,7 @@ enabled on the Git server:
|
|||
many applications, each in a different subdirectory in the root. Create a file
|
||||
`shiny-app/.filterspec` using the GitLab web interface:
|
||||
|
||||
```.gitignore
|
||||
```plaintext
|
||||
# Only the paths listed in the file will be downloaded when performing a
|
||||
# partial clone using `--filter=sparse:oid=shiny-app/.gitfilterspec`
|
||||
|
||||
|
@ -136,7 +138,7 @@ enabled on the Git server:
|
|||
shared-component-b/
|
||||
```
|
||||
|
||||
1. *Create a new Git repository and fetch.* Support for `--filter=sparse:oid`
|
||||
1. **Create a new Git repository and fetch.** Support for `--filter=sparse:oid`
|
||||
using the clone command is incomplete, so we will emulate the clone command
|
||||
by hand, using `git init` and `git fetch`. Follow
|
||||
[issue tracking support for `--filter=sparse:oid`](https://gitlab.com/gitlab-org/git/issues/4)
|
||||
|
|
|
@ -63,8 +63,11 @@ For more details on the specific data persisted in a group export, see the
|
|||
|
||||

|
||||
|
||||
1. Once the export is generated, you can click **Download export** to download the [exported contents](#exported-contents)
|
||||
in a compressed tar archive, with contents in JSON format. You can also return to this page to regenerate the export data.
|
||||
1. Once the export is generated, you should receive an e-mail with a link to the [exported contents](#exported-contents)
|
||||
in a compressed tar archive, with contents in JSON format.
|
||||
|
||||
1. Alternatively, you can come back to the project settings and download the
|
||||
file from there by clicking **Download export**, or generate a new file by clicking **Regenerate export**.
|
||||
|
||||
## Rate Limits
|
||||
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/issues/199370) from **Settings > Repository** in GitLab 12.9.
|
||||
> - [Added `write_registry` scope](https://gitlab.com/gitlab-org/gitlab/-/issues/22743) in GitLab 12.10.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29280) from **Settings > CI / CD** in GitLab 12.10.1.
|
||||
> - [Added package registry scopes](https://gitlab.com/gitlab-org/gitlab/-/issues/213566) from **Settings > CI / CD** in GitLab 13.0.
|
||||
|
||||
Deploy tokens allow you to download (`git clone`) or push and pull the container registry images of a project without having a user and a password.
|
||||
Deploy tokens allow you to download (`git clone`) or push and pull packages and container registry images of a project without having a user and a password.
|
||||
|
||||
Deploy tokens can be managed by [maintainers only](../../permissions.md).
|
||||
|
||||
|
@ -101,6 +102,22 @@ To push the container registry images, you'll need to:
|
|||
Just replace `<username>` and `<deploy_token>` with the proper values. Then you can simply
|
||||
push images to your Container Registry.
|
||||
|
||||
### Read or pull packages
|
||||
|
||||
To pull packages in the GitLab package registry, you'll need to:
|
||||
|
||||
1. Create a Deploy Token with `read_package_registry` as a scope.
|
||||
1. Take note of your `username` and `token`.
|
||||
1. For the [package type of your choice](./../../packages/index.md), follow the authentication instructions for deploy tokens.
|
||||
|
||||
### Push or upload packages
|
||||
|
||||
To upload packages in the GitLab package registry, you'll need to:
|
||||
|
||||
1. Create a Deploy Token with `write_package_registry` as a scope.
|
||||
1. Take note of your `username` and `token`.
|
||||
1. For the [package type of your choice](./../../packages/index.md), follow the authentication instructions for deploy tokens.
|
||||
|
||||
### Group Deploy Token
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/21765) in GitLab 12.9.
|
||||
|
|
|
@ -11,6 +11,8 @@ module API
|
|||
result_hash = Hashie::Mash.new
|
||||
result_hash[:read_registry] = scopes.include?('read_registry')
|
||||
result_hash[:write_registry] = scopes.include?('write_registry')
|
||||
result_hash[:read_package_registry] = scopes.include?('read_package_registry')
|
||||
result_hash[:write_package_registry] = scopes.include?('write_package_registry')
|
||||
result_hash[:read_repository] = scopes.include?('read_repository')
|
||||
result_hash
|
||||
end
|
||||
|
@ -55,7 +57,7 @@ module API
|
|||
params do
|
||||
requires :name, type: String, desc: "New deploy token's name"
|
||||
requires :scopes, type: Array[String], values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
|
||||
desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", or "write_registry".'
|
||||
desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", "write_registry", "read_package_registry", or "write_package_registry".'
|
||||
optional :expires_at, type: DateTime, desc: 'Expiration date for the deploy token. Does not expire if no value is provided.'
|
||||
optional :username, type: String, desc: 'Username for deploy token. Default is `gitlab+deploy-token-{n}`'
|
||||
end
|
||||
|
@ -118,7 +120,7 @@ module API
|
|||
params do
|
||||
requires :name, type: String, desc: 'The name of the deploy token'
|
||||
requires :scopes, type: Array[String], values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
|
||||
desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", or "write_registry".'
|
||||
desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", "write_registry", "read_package_registry", or "write_package_registry".'
|
||||
optional :expires_at, type: DateTime, desc: 'Expiration date for the deploy token. Does not expire if no value is provided.'
|
||||
optional :username, type: String, desc: 'Username for deploy token. Default is `gitlab+deploy-token-{n}`'
|
||||
end
|
||||
|
|
|
@ -7128,12 +7128,18 @@ msgstr ""
|
|||
msgid "DeployTokens|Add a deploy token"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Allows read access to the package registry"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Allows read-only access to the registry images"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Allows read-only access to the repository"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Allows write access to the package registry"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Allows write access to the registry images"
|
||||
msgstr ""
|
||||
|
||||
|
@ -7155,13 +7161,13 @@ msgstr ""
|
|||
msgid "DeployTokens|Deploy Tokens"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Deploy tokens allow access to your repository and registry images."
|
||||
msgid "DeployTokens|Deploy tokens allow access to packages, your repository, and registry images."
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Expires"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Group deploy tokens allow read-only access to the repositories and registry images within the group."
|
||||
msgid "DeployTokens|Group deploy tokens allow access to the packages, repositories, and registry images within the group."
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Name"
|
||||
|
@ -10496,6 +10502,12 @@ msgstr ""
|
|||
msgid "Group"
|
||||
msgstr ""
|
||||
|
||||
msgid "Group %{group_name} couldn't be exported."
|
||||
msgstr ""
|
||||
|
||||
msgid "Group %{group_name} was exported successfully."
|
||||
msgstr ""
|
||||
|
||||
msgid "Group %{group_name} was scheduled for deletion."
|
||||
msgstr ""
|
||||
|
||||
|
@ -10547,10 +10559,13 @@ msgstr ""
|
|||
msgid "Group export could not be started."
|
||||
msgstr ""
|
||||
|
||||
msgid "Group export error"
|
||||
msgstr ""
|
||||
|
||||
msgid "Group export link has expired. Please generate a new export from your group settings."
|
||||
msgstr ""
|
||||
|
||||
msgid "Group export started."
|
||||
msgid "Group export started. A download link will be sent by email."
|
||||
msgstr ""
|
||||
|
||||
msgid "Group has been already marked for deletion"
|
||||
|
@ -10595,6 +10610,9 @@ msgstr ""
|
|||
msgid "Group variables (inherited)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Group was exported"
|
||||
msgstr ""
|
||||
|
||||
msgid "Group was successfully updated."
|
||||
msgstr ""
|
||||
|
||||
|
@ -14492,9 +14510,6 @@ msgstr ""
|
|||
msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
|
||||
msgstr ""
|
||||
|
||||
msgid "Once the exported file is ready you can download it from this page."
|
||||
msgstr ""
|
||||
|
||||
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
|
||||
msgstr ""
|
||||
|
||||
|
@ -21027,9 +21042,15 @@ msgstr ""
|
|||
msgid "The domain you entered is not allowed."
|
||||
msgstr ""
|
||||
|
||||
msgid "The download link will expire in 24 hours."
|
||||
msgstr ""
|
||||
|
||||
msgid "The entered user map is not a valid JSON user map."
|
||||
msgstr ""
|
||||
|
||||
msgid "The errors we encountered were:"
|
||||
msgstr ""
|
||||
|
||||
msgid "The file has been successfully created."
|
||||
msgstr ""
|
||||
|
||||
|
@ -21068,6 +21089,9 @@ msgstr ""
|
|||
msgid "The group can be fully restored"
|
||||
msgstr ""
|
||||
|
||||
msgid "The group export can be downloaded from:"
|
||||
msgstr ""
|
||||
|
||||
msgid "The group has already been shared with this group"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Performance
|
||||
class ARExistsAndPresentBlank < RuboCop::Cop::Cop
|
||||
def message_present(ivar)
|
||||
"Avoid `#{ivar}.present?`, because it will generate database query 'Select TABLE.*' which is expensive. "\
|
||||
"Suggest to use `#{ivar}.any?` to replace `#{ivar}.present?`"
|
||||
end
|
||||
|
||||
def message_blank(ivar)
|
||||
"Avoid `#{ivar}.blank?`, because it will generate database query 'Select TABLE.*' which is expensive. "\
|
||||
"Suggest to use `#{ivar}.empty?` to replace `#{ivar}.blank?`"
|
||||
end
|
||||
|
||||
def_node_matcher :exists_match, <<~PATTERN
|
||||
(send (ivar $_) :exists?)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :present_match, <<~PATTERN
|
||||
(send (ivar $_) :present?)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :blank_match, <<~PATTERN
|
||||
(send (ivar $_) :blank?)
|
||||
PATTERN
|
||||
|
||||
def file_name(node)
|
||||
node.location.expression.source_buffer.name
|
||||
end
|
||||
|
||||
def in_haml_file?(node)
|
||||
file_name(node).end_with?('.haml.rb')
|
||||
end
|
||||
|
||||
def on_send(node)
|
||||
return unless in_haml_file?(node)
|
||||
|
||||
ivar_present = present_match(node)
|
||||
ivar_blank = blank_match(node)
|
||||
return unless ivar_present || ivar_blank
|
||||
|
||||
node.each_ancestor(:begin) do |begin_node|
|
||||
begin_node.each_descendant do |n|
|
||||
ivar_exists = exists_match(n)
|
||||
next unless ivar_exists
|
||||
|
||||
add_offense(node, location: :expression, message: message_present(ivar_exists)) if ivar_exists == ivar_present
|
||||
add_offense(node, location: :expression, message: message_blank(ivar_exists)) if ivar_exists == ivar_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -110,10 +110,6 @@ describe('AlertDetails', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('renders a status dropdown containing three items', () => {
|
||||
expect(wrapper.findAll('[data-testid="statusDropdownItem"]').length).toBe(3);
|
||||
});
|
||||
|
||||
describe('Create issue from alert', () => {
|
||||
describe('createIssueFromAlertEnabled feature flag enabled', () => {
|
||||
it('should display a button that links to new issue page', () => {
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'email_spec'
|
||||
|
||||
describe Emails::Groups do
|
||||
include EmailSpec::Matchers
|
||||
|
||||
let(:group) { create(:group) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
describe '#group_was_exported_email' do
|
||||
subject { Notify.group_was_exported_email(user, group) }
|
||||
|
||||
it 'sends success email' do
|
||||
expect(subject).to have_subject "#{group.name} | Group was exported"
|
||||
expect(subject).to have_body_text 'The download link will expire in 24 hours.'
|
||||
expect(subject).to have_body_text "groups/#{group.path}/-/download_export"
|
||||
end
|
||||
end
|
||||
|
||||
describe '#group_was_not_exported_email' do
|
||||
let(:shared) { Gitlab::ImportExport::Shared.new(group) }
|
||||
let(:error) { Gitlab::ImportExport::Error.new('Error!') }
|
||||
|
||||
before do
|
||||
shared.error(error)
|
||||
end
|
||||
|
||||
subject { Notify.group_was_not_exported_email(user, group, shared.errors) }
|
||||
|
||||
it 'sends failure email' do
|
||||
expect(subject).to have_subject "#{group.name} | Group export error"
|
||||
expect(subject).to have_body_text "Group #{group.name} couldn't be exported."
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,111 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require_relative '../../../support/helpers/expect_offense'
|
||||
require_relative '../../../../rubocop/cop/performance/ar_exists_and_present_blank.rb'
|
||||
|
||||
describe RuboCop::Cop::Performance::ARExistsAndPresentBlank do
|
||||
include CopHelper
|
||||
include ExpectOffense
|
||||
|
||||
subject(:cop) { described_class.new }
|
||||
|
||||
context 'when it is not haml file' do
|
||||
it 'does not flag it as an offense' do
|
||||
expect(subject).to receive(:in_haml_file?).with(anything).at_least(:once).and_return(false)
|
||||
|
||||
expect_no_offenses <<~SOURCE
|
||||
return unless @users.exists?
|
||||
show @users if @users.present?
|
||||
SOURCE
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is haml file' do
|
||||
before do
|
||||
expect(subject).to receive(:in_haml_file?).with(anything).at_least(:once).and_return(true)
|
||||
end
|
||||
|
||||
context 'the same object uses exists? and present?' do
|
||||
it 'flags it as an offense' do
|
||||
expect_offense <<~SOURCE
|
||||
return unless @users.exists?
|
||||
show @users if @users.present?
|
||||
^^^^^^^^^^^^^^^ Avoid `@users.present?`, because it will generate database query 'Select TABLE.*' which is expensive. Suggest to use `@users.any?` to replace `@users.present?`
|
||||
SOURCE
|
||||
|
||||
expect(cop.offenses.map(&:cop_name)).to contain_exactly('Performance/ARExistsAndPresentBlank')
|
||||
end
|
||||
end
|
||||
|
||||
context 'the same object uses exists? and blank?' do
|
||||
it 'flags it as an offense' do
|
||||
expect_offense <<~SOURCE
|
||||
return unless @users.exists?
|
||||
show @users if @users.blank?
|
||||
^^^^^^^^^^^^^ Avoid `@users.blank?`, because it will generate database query 'Select TABLE.*' which is expensive. Suggest to use `@users.empty?` to replace `@users.blank?`
|
||||
SOURCE
|
||||
|
||||
expect(cop.offenses.map(&:cop_name)).to contain_exactly('Performance/ARExistsAndPresentBlank')
|
||||
end
|
||||
end
|
||||
|
||||
context 'the same object uses exists?, blank? and present?' do
|
||||
it 'flags it as an offense' do
|
||||
expect_offense <<~SOURCE
|
||||
return unless @users.exists?
|
||||
show @users if @users.blank?
|
||||
^^^^^^^^^^^^^ Avoid `@users.blank?`, because it will generate database query 'Select TABLE.*' which is expensive. Suggest to use `@users.empty?` to replace `@users.blank?`
|
||||
show @users if @users.present?
|
||||
^^^^^^^^^^^^^^^ Avoid `@users.present?`, because it will generate database query 'Select TABLE.*' which is expensive. Suggest to use `@users.any?` to replace `@users.present?`
|
||||
SOURCE
|
||||
|
||||
expect(cop.offenses.map(&:cop_name)).to contain_exactly('Performance/ARExistsAndPresentBlank', 'Performance/ARExistsAndPresentBlank')
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'different object uses exists? and present?/blank?' do |another_method|
|
||||
it 'does not flag it as an offense' do
|
||||
expect_no_offenses <<~SOURCE
|
||||
return unless @users.exists?
|
||||
present @emails if @emails.#{another_method}
|
||||
SOURCE
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'different object uses exists? and present?/blank?', 'present?'
|
||||
it_behaves_like 'different object uses exists? and present?/blank?', 'blank?'
|
||||
|
||||
RSpec.shared_examples 'Only using one present?/blank? without exists?' do |non_exists_method|
|
||||
it 'does not flag it as an offense' do
|
||||
expect_no_offenses "@users.#{non_exists_method}"
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'Only using one present?/blank? without exists?', 'present?'
|
||||
it_behaves_like 'Only using one present?/blank? without exists?', 'blank?'
|
||||
|
||||
context 'when using many present?/empty? without exists?' do
|
||||
it 'does not flag it as an offense' do
|
||||
expect_no_offenses <<~SOURCE
|
||||
@user.present?
|
||||
@user.blank?
|
||||
@user.present?
|
||||
@user.blank?
|
||||
SOURCE
|
||||
end
|
||||
end
|
||||
|
||||
context 'when just using exists? without present?/blank?' do
|
||||
it 'does not flag it as an offense' do
|
||||
expect_no_offenses '@users.exists?'
|
||||
|
||||
expect_no_offenses <<~SOURCE
|
||||
@users.exists?
|
||||
@users.some_other_method?
|
||||
@users.exists?
|
||||
SOURCE
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -71,6 +71,14 @@ describe Groups::ImportExport::ExportService do
|
|||
service.execute
|
||||
end
|
||||
|
||||
it 'notifies the user' do
|
||||
expect_next_instance_of(NotificationService) do |instance|
|
||||
expect(instance).to receive(:group_was_exported)
|
||||
end
|
||||
|
||||
service.execute
|
||||
end
|
||||
|
||||
context 'when saver succeeds' do
|
||||
it 'saves the group in the file system' do
|
||||
service.execute
|
||||
|
@ -114,16 +122,26 @@ describe Groups::ImportExport::ExportService do
|
|||
|
||||
context 'when export fails' do
|
||||
context 'when file saver fails' do
|
||||
it 'removes the remaining exported data' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::ImportExport::Saver) do |saver|
|
||||
allow(saver).to receive(:save).and_return(false)
|
||||
end
|
||||
end
|
||||
|
||||
it 'removes the remaining exported data' do
|
||||
expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
|
||||
|
||||
expect(group.import_export_upload).to be_nil
|
||||
expect(File.exist?(shared.archive_path)).to eq(false)
|
||||
end
|
||||
|
||||
it 'notifies the user about failed group export' do
|
||||
expect_next_instance_of(NotificationService) do |instance|
|
||||
expect(instance).to receive(:group_was_not_exported)
|
||||
end
|
||||
|
||||
expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when file compression fails' do
|
||||
|
|
Loading…
Reference in New Issue