Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1123408ec8
commit
4db74ea147
26 changed files with 568 additions and 150 deletions
|
@ -291,6 +291,12 @@ Gitlab/AvoidUploadedFileFromParams:
|
||||||
- 'spec/**/*'
|
- 'spec/**/*'
|
||||||
- 'ee/spec/**/*'
|
- 'ee/spec/**/*'
|
||||||
|
|
||||||
|
Gitlab/EventStoreSubscriber:
|
||||||
|
Enabled: true
|
||||||
|
Exclude:
|
||||||
|
- 'spec/**/*'
|
||||||
|
- 'ee/spec/**/*'
|
||||||
|
|
||||||
GitlabSecurity/PublicSend:
|
GitlabSecurity/PublicSend:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
Exclude:
|
Exclude:
|
||||||
|
|
|
@ -41,6 +41,16 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
inputFieldName: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'upload_file',
|
||||||
|
},
|
||||||
|
shouldUpdateInputOnFileDrop: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -84,6 +94,30 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: This is a temporary solution to integrate dropzone into a Rails
|
||||||
|
// form. On file drop if `shouldUpdateInputOnFileDrop` is true, the file
|
||||||
|
// input value is updated. So that when the form is submitted — the file
|
||||||
|
// value would be send together with the form data. This solution should
|
||||||
|
// be removed when License file upload page is fully migrated:
|
||||||
|
// https://gitlab.com/gitlab-org/gitlab/-/issues/352501
|
||||||
|
// NOTE: as per https://caniuse.com/mdn-api_htmlinputelement_files, IE11
|
||||||
|
// is not able to set input.files property, thought the user would still
|
||||||
|
// be able to use the file picker dialogue option, by clicking the
|
||||||
|
// "openFileUpload" button
|
||||||
|
if (this.shouldUpdateInputOnFileDrop) {
|
||||||
|
// Since FileList cannot be easily manipulated, to match requirement of
|
||||||
|
// singleFileSelection, we're throwing an error if multiple files were
|
||||||
|
// dropped on the dropzone
|
||||||
|
// NOTE: we can drop this logic together with
|
||||||
|
// `shouldUpdateInputOnFileDrop` flag
|
||||||
|
if (this.singleFileSelection && files.length > 1) {
|
||||||
|
this.$emit('error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$refs.fileUpload.files = files;
|
||||||
|
}
|
||||||
|
|
||||||
this.$emit('change', this.singleFileSelection ? files[0] : files);
|
this.$emit('change', this.singleFileSelection ? files[0] : files);
|
||||||
},
|
},
|
||||||
ondragenter(e) {
|
ondragenter(e) {
|
||||||
|
@ -116,6 +150,7 @@ export default {
|
||||||
<slot>
|
<slot>
|
||||||
<button
|
<button
|
||||||
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
||||||
|
type="button"
|
||||||
@click="openFileUpload"
|
@click="openFileUpload"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -147,7 +182,7 @@ export default {
|
||||||
<input
|
<input
|
||||||
ref="fileUpload"
|
ref="fileUpload"
|
||||||
type="file"
|
type="file"
|
||||||
name="upload_file"
|
:name="inputFieldName"
|
||||||
:accept="validFileMimetypes"
|
:accept="validFileMimetypes"
|
||||||
class="hide"
|
class="hide"
|
||||||
:multiple="!singleFileSelection"
|
:multiple="!singleFileSelection"
|
||||||
|
|
|
@ -15,6 +15,7 @@ class ContainerRepository < ApplicationRecord
|
||||||
MIGRATION_STATES = (IDLE_MIGRATION_STATES + ACTIVE_MIGRATION_STATES).freeze
|
MIGRATION_STATES = (IDLE_MIGRATION_STATES + ACTIVE_MIGRATION_STATES).freeze
|
||||||
|
|
||||||
TooManyImportsError = Class.new(StandardError)
|
TooManyImportsError = Class.new(StandardError)
|
||||||
|
NativeImportError = Class.new(StandardError)
|
||||||
|
|
||||||
belongs_to :project
|
belongs_to :project
|
||||||
|
|
||||||
|
@ -85,9 +86,7 @@ class ContainerRepository < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
state :pre_import_done do
|
state :pre_import_done do
|
||||||
validates :migration_pre_import_started_at,
|
validates :migration_pre_import_done_at, presence: true
|
||||||
:migration_pre_import_done_at,
|
|
||||||
presence: true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
state :importing do
|
state :importing do
|
||||||
|
@ -113,7 +112,7 @@ class ContainerRepository < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
event :finish_pre_import do
|
event :finish_pre_import do
|
||||||
transition pre_importing: :pre_import_done
|
transition %i[pre_importing import_aborted] => :pre_import_done
|
||||||
end
|
end
|
||||||
|
|
||||||
event :start_import do
|
event :start_import do
|
||||||
|
@ -121,7 +120,7 @@ class ContainerRepository < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
event :finish_import do
|
event :finish_import do
|
||||||
transition importing: :import_done
|
transition %i[importing import_aborted] => :import_done
|
||||||
end
|
end
|
||||||
|
|
||||||
event :already_migrated do
|
event :already_migrated do
|
||||||
|
@ -155,7 +154,7 @@ class ContainerRepository < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
before_transition pre_importing: :pre_import_done do |container_repository|
|
before_transition %i[pre_importing import_aborted] => :pre_import_done do |container_repository|
|
||||||
container_repository.migration_pre_import_done_at = Time.zone.now
|
container_repository.migration_pre_import_done_at = Time.zone.now
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -170,7 +169,7 @@ class ContainerRepository < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
before_transition importing: :import_done do |container_repository|
|
before_transition %i[importing import_aborted] => :import_done do |container_repository|
|
||||||
container_repository.migration_import_done_at = Time.zone.now
|
container_repository.migration_import_done_at = Time.zone.now
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -272,13 +271,29 @@ class ContainerRepository < ApplicationRecord
|
||||||
finish_pre_import && start_import
|
finish_pre_import && start_import
|
||||||
end
|
end
|
||||||
|
|
||||||
def retry_migration
|
def retry_aborted_migration
|
||||||
return if migration_import_done_at
|
return unless migration_state == 'import_aborted'
|
||||||
|
|
||||||
if migration_pre_import_done_at
|
import_status = gitlab_api_client.import_status(self.path)
|
||||||
|
|
||||||
|
case import_status
|
||||||
|
when 'native'
|
||||||
|
raise NativeImportError
|
||||||
|
when 'import_in_progress'
|
||||||
|
nil
|
||||||
|
when 'import_complete'
|
||||||
|
finish_import
|
||||||
|
when 'import_failed'
|
||||||
retry_import
|
retry_import
|
||||||
else
|
when 'pre_import_in_progress'
|
||||||
|
nil
|
||||||
|
when 'pre_import_complete'
|
||||||
|
finish_pre_import_and_start_import
|
||||||
|
when 'pre_import_failed'
|
||||||
retry_pre_import
|
retry_pre_import
|
||||||
|
else
|
||||||
|
# If the import_status request fails, use the timestamp to guess current state
|
||||||
|
migration_pre_import_done_at ? retry_import : retry_pre_import
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,10 @@
|
||||||
%a{ href: help_page_path('user/usage_quotas.md'), target: '_blank', rel: 'noopener noreferrer' }
|
%a{ href: help_page_path('user/usage_quotas.md'), target: '_blank', rel: 'noopener noreferrer' }
|
||||||
= s_('UsageQuota|Learn more about usage quotas') + '.'
|
= s_('UsageQuota|Learn more about usage quotas') + '.'
|
||||||
|
|
||||||
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
|
= gl_tabs_nav do
|
||||||
%ul.nav.nav-tabs.nav-links.scrolling-tabs.separator.js-usage-quota-tabs{ role: 'tablist' }
|
= gl_tab_link_to '#storage-quota-tab', item_active: true do
|
||||||
%li.nav-item
|
= s_('UsageQuota|Storage')
|
||||||
%a.nav-link#storage-quota{ data: { toggle: "tab", action: '#storage-quota-tab' }, href: '#storage-quota-tab', 'aria-controls': '#storage-quota-tab', 'aria-selected': 'true' }
|
|
||||||
= s_('UsageQuota|Storage')
|
|
||||||
.tab-content
|
.tab-content
|
||||||
.tab-pane#storage-quota-tab
|
.tab-pane.active#storage-quota-tab
|
||||||
#js-project-storage-count-app{ data: { project_path: @project.full_path } }
|
#js-project-storage-count-app{ data: { project_path: @project.full_path } }
|
||||||
|
|
|
@ -32,7 +32,7 @@ module ContainerRegistry
|
||||||
private
|
private
|
||||||
|
|
||||||
def handle_aborted_migration
|
def handle_aborted_migration
|
||||||
return unless next_aborted_repository&.retry_migration
|
return unless next_aborted_repository&.retry_aborted_migration
|
||||||
|
|
||||||
log_extra_metadata_on_done(:container_repository_id, next_aborted_repository.id)
|
log_extra_metadata_on_done(:container_repository_id, next_aborted_repository.id)
|
||||||
log_extra_metadata_on_done(:import_type, 'retry')
|
log_extra_metadata_on_done(:import_type, 'retry')
|
||||||
|
|
|
@ -4,77 +4,69 @@ group: Distribution
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||||
---
|
---
|
||||||
|
|
||||||
# How to self-host the docs site **(FREE SELF)**
|
# How to host the GitLab product documentation **(FREE SELF)**
|
||||||
|
|
||||||
If you have a self-managed instance of GitLab, you may not be able to access the
|
If you are not able to access the GitLab product documentation at `docs.gitlab.com`,
|
||||||
product documentation as hosted on `docs.gitlab.com` from your GitLab instance.
|
you can host the documentation yourself instead.
|
||||||
|
|
||||||
Be aware of the following items if you self-host the product documentation:
|
|
||||||
|
|
||||||
- You must host the product documentation site under a subdirectory that matches
|
|
||||||
your installed GitLab version (for example, `14.5/`). The
|
|
||||||
[Docker images](https://gitlab.com/gitlab-org/gitlab-docs/container_registry/631635)
|
|
||||||
hosted by the GitLab Docs team provide this by default. We use a
|
|
||||||
[script](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/2995d1378175803b22fb8806ba77adf63e79f32c/scripts/normalize-links.sh#L28-82)
|
|
||||||
to normalize the links and prefix them with the respective version.
|
|
||||||
- The version dropdown will display additional versions that don't exist, selecting
|
|
||||||
those versions will display a 404 Not Found page.
|
|
||||||
- Results when using the search box will display results from `docs.gitlab.com`
|
|
||||||
and not the local documentation.
|
|
||||||
- When you use the Docker images to serve the product documentation site, by
|
|
||||||
default the landing page redirects to the respective version (for example, `/14.5/`),
|
|
||||||
which causes the landing page <https://docs.gitlab.com> to not be displayed.
|
|
||||||
|
|
||||||
## Documentation self-hosting options
|
## Documentation self-hosting options
|
||||||
|
|
||||||
You can self-host the GitLab product documentation locally using one of these
|
To host the GitLab product documentation, you can use:
|
||||||
methods:
|
|
||||||
|
|
||||||
- Docker
|
- A Docker container
|
||||||
- GitLab Pages
|
- GitLab Pages
|
||||||
- From your own webserver
|
- Your own web server
|
||||||
|
|
||||||
The examples on this page are based on GitLab 14.5.
|
After you create a website by using one of these methods, you redirect the UI links
|
||||||
|
in the product to point to your website.
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
The website you create must be hosted under a subdirectory that matches
|
||||||
|
your installed GitLab version (for example, `14.5/`). The
|
||||||
|
[Docker images](https://gitlab.com/gitlab-org/gitlab-docs/container_registry/631635)
|
||||||
|
use this version by default.
|
||||||
|
|
||||||
|
The following examples use GitLab 14.5.
|
||||||
|
|
||||||
### Self-host the product documentation with Docker
|
### Self-host the product documentation with Docker
|
||||||
|
|
||||||
The Docker images use a built-in webserver listening on port `4000`, so you need
|
You can run the GitLab product documentation website in a Docker container:
|
||||||
to expose that.
|
|
||||||
|
|
||||||
In the server that you host GitLab, or any other server that your GitLab instance
|
1. Expose port `4000`. The Docker image uses this port for the web server.
|
||||||
can talk to, you can use Docker to pull the docs site:
|
1. On the server where you host GitLab, or on any other server that your GitLab instance
|
||||||
|
can communicate with, pull the docs site:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker run -it --rm -p 4000:4000 registry.gitlab.com/gitlab-org/gitlab-docs:14.5
|
docker run -it --rm -p 4000:4000 registry.gitlab.com/gitlab-org/gitlab-docs:14.5
|
||||||
```
|
```
|
||||||
|
|
||||||
If you use [Docker compose](../install/docker.md#install-gitlab-using-docker-compose)
|
If you host your GitLab instance using [Docker compose](../install/docker.md#install-gitlab-using-docker-compose),
|
||||||
to host your GitLab instance, add the following to `docker-compose.yaml`:
|
add the following to `docker-compose.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: '3.6'
|
version: '3.6'
|
||||||
services:
|
services:
|
||||||
docs:
|
docs:
|
||||||
image: registry.gitlab.com/gitlab-org/gitlab-docs:14.5
|
image: registry.gitlab.com/gitlab-org/gitlab-docs:14.5
|
||||||
hostname: 'https://gitlab.example.com'
|
hostname: 'https://gitlab.example.com'
|
||||||
ports:
|
ports:
|
||||||
- '4000:4000'
|
- '4000:4000'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Self-host the product documentation with GitLab Pages
|
### Self-host the product documentation with GitLab Pages
|
||||||
|
|
||||||
You use GitLab Pages to host the GitLab product documentation locally.
|
You can use GitLab Pages to host the GitLab product documentation.
|
||||||
|
|
||||||
Prerequisite:
|
Prerequisite:
|
||||||
|
|
||||||
- The Pages site URL must not use a subfolder. Due to the nature of how the docs
|
- Ensure the Pages site URL does not use a subfolder. Because of how the docs
|
||||||
site is pre-compiled, the CSS and JavaScript files are relative to the
|
site is pre-compiled, the CSS and JavaScript files are relative to the
|
||||||
main domain or subdomain. For example, URLs like `https://example.com/docs/`
|
main domain or subdomain. For example, URLs like `https://example.com/docs/`
|
||||||
are not supported.
|
are not supported.
|
||||||
|
|
||||||
To host the product documentation site with GitLab Pages:
|
To host the product documentation site with GitLab Pages:
|
||||||
|
|
||||||
1. [Create a new blank project](../user/project/working_with_projects.md#create-a-blank-project).
|
1. [Create a blank project](../user/project/working_with_projects.md#create-a-blank-project).
|
||||||
1. Create a new or edit your existing `.gitlab-ci.yml` file, and add the following
|
1. Create a new or edit your existing `.gitlab-ci.yml` file, and add the following
|
||||||
`pages` job, while ensuring the version is the same as your GitLab installation:
|
`pages` job, while ensuring the version is the same as your GitLab installation:
|
||||||
|
|
||||||
|
@ -97,13 +89,13 @@ To host the product documentation site with GitLab Pages:
|
||||||
| [Project website](../user/project/pages/getting_started_part_one.md#project-website-examples) | Not supported | Supported |
|
| [Project website](../user/project/pages/getting_started_part_one.md#project-website-examples) | Not supported | Supported |
|
||||||
| [User or group website](../user/project/pages/getting_started_part_one.md#user-and-group-website-examples) | Supported | Supported |
|
| [User or group website](../user/project/pages/getting_started_part_one.md#user-and-group-website-examples) | Supported | Supported |
|
||||||
|
|
||||||
### Self-host the product documentation on your own webserver
|
### Self-host the product documentation on your own web server
|
||||||
|
|
||||||
Because the product documentation site is static, you can grab the directory from
|
Because the product documentation site is static, from the container, you can take the contents
|
||||||
the container (in `/usr/share/nginx/html`) and use your own web server to host
|
of `/usr/share/nginx/html` and use your own web server to host
|
||||||
it wherever you want.
|
the docs wherever you want.
|
||||||
|
|
||||||
Use the following commands, and replace `<destination>` with the directory where the
|
Run the following commands, replacing `<destination>` with the directory where the
|
||||||
documentation files will be copied to:
|
documentation files will be copied to:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -114,18 +106,30 @@ docker rm -f gitlab-docs
|
||||||
|
|
||||||
## Redirect the `/help` links to the new docs page
|
## Redirect the `/help` links to the new docs page
|
||||||
|
|
||||||
After your local product documentation site is running, [redirect the help
|
After your local product documentation site is running,
|
||||||
links](../user/admin_area/settings/help_page.md#redirect-help-pages) in the GitLab
|
[redirect the help links](../user/admin_area/settings/help_page.md#redirect-help-pages)
|
||||||
application to your local site.
|
in the GitLab application to your local site.
|
||||||
|
|
||||||
Be sure to use the fully qualified domain name as the docs URL. For example, if you
|
Be sure to use the fully qualified domain name as the docs URL. For example, if you
|
||||||
used the [Docker method](#self-host-the-product-documentation-with-docker), enter `http://0.0.0.0:4000`.
|
used the [Docker method](#self-host-the-product-documentation-with-docker), enter `http://0.0.0.0:4000`.
|
||||||
|
|
||||||
You don't need to append the version, as GitLab will detect it and append it to
|
You don't need to append the version. GitLab detects it and appends it to
|
||||||
any documentation URL requests, as needed. For example, if your GitLab version is
|
documentation URL requests as needed. For example, if your GitLab version is
|
||||||
14.5, the GitLab Docs URL becomes `http://0.0.0.0:4000/14.5/`. The link
|
14.5:
|
||||||
inside GitLab displays as `<instance_url>/help/user/admin_area/settings/help_page#destination-requirements`,
|
|
||||||
but when you select it, you are redirected to
|
- The GitLab Docs URL becomes `http://0.0.0.0:4000/14.5/`.
|
||||||
|
- The link in GitLab displays as `<instance_url>/help/user/admin_area/settings/help_page#destination-requirements`.
|
||||||
|
- When you select the link, you are redirected to
|
||||||
`http://0.0.0.0:4000/14.5/ee/user/admin_area/settings/help_page/#destination-requirements`.
|
`http://0.0.0.0:4000/14.5/ee/user/admin_area/settings/help_page/#destination-requirements`.
|
||||||
|
|
||||||
To test the setting, select a **Learn more** link within the GitLab application.
|
To test the setting, select a **Learn more** link within the GitLab application.
|
||||||
|
|
||||||
|
## Known issues
|
||||||
|
|
||||||
|
If you self-host the product documentation:
|
||||||
|
|
||||||
|
- The version dropdown displays additional versions that don't exist. Selecting
|
||||||
|
these versions displays a `404 Not Found` page.
|
||||||
|
- The search displays results from `docs.gitlab.com` and not the local site.
|
||||||
|
- By default, the landing page redirects to the
|
||||||
|
respective version (for example, `/14.5/`). This causes the landing page <https://docs.gitlab.com> to not be displayed.
|
||||||
|
|
|
@ -62,8 +62,8 @@ can reserve your catch-all mailbox for other purposes.
|
||||||
|
|
||||||
### Dedicated email address
|
### Dedicated email address
|
||||||
|
|
||||||
This solution is relatively simple to set up: you just need to create an email
|
To set up this solution, you must create a dedicated email
|
||||||
address dedicated to receive your users' replies to GitLab notifications. However,
|
address to receive your users' replies to GitLab notifications. However,
|
||||||
this method only supports replies, and not the other features of [incoming email](#incoming-email).
|
this method only supports replies, and not the other features of [incoming email](#incoming-email).
|
||||||
|
|
||||||
## Accepted headers
|
## Accepted headers
|
||||||
|
@ -75,7 +75,7 @@ Email is processed correctly when a configured email address is present in one o
|
||||||
- `Envelope-To` or `X-Envelope-To`
|
- `Envelope-To` or `X-Envelope-To`
|
||||||
|
|
||||||
In GitLab 14.6 and later, [Service Desk](../user/project/service_desk.md)
|
In GitLab 14.6 and later, [Service Desk](../user/project/service_desk.md)
|
||||||
also checks these additional headers.
|
also checks accepted headers.
|
||||||
|
|
||||||
Usually, the "To" field contains the email address of the primary receiver.
|
Usually, the "To" field contains the email address of the primary receiver.
|
||||||
However, it might not include the configured GitLab email address if:
|
However, it might not include the configured GitLab email address if:
|
||||||
|
@ -84,6 +84,14 @@ However, it might not include the configured GitLab email address if:
|
||||||
- The address was included when using "Reply all".
|
- The address was included when using "Reply all".
|
||||||
- The email was forwarded.
|
- The email was forwarded.
|
||||||
|
|
||||||
|
## Rejected headers
|
||||||
|
|
||||||
|
To prevent unwanted issue creation from automatic email systems, GitLab ignores all incoming email
|
||||||
|
containing the following headers:
|
||||||
|
|
||||||
|
- `Auto-Submitted` with a value other than `no`
|
||||||
|
- `X-Autoreply` with a value of `yes`
|
||||||
|
|
||||||
## Set it up
|
## Set it up
|
||||||
|
|
||||||
If you want to use Gmail / Google Apps for incoming email, make sure you have
|
If you want to use Gmail / Google Apps for incoming email, make sure you have
|
||||||
|
|
|
@ -97,6 +97,7 @@ The [Quality Engineering - Enablement team](https://about.gitlab.com/handbook/en
|
||||||
- Network latency on the test environments between components on all Cloud Providers were measured at <5ms. Note that this is shared as an observation and not as an implicit recommendation.
|
- Network latency on the test environments between components on all Cloud Providers were measured at <5ms. Note that this is shared as an observation and not as an implicit recommendation.
|
||||||
- We aim to have a "test smart" approach where architectures tested have a good range that can also apply to others. Testing focuses on 10k Omnibus on GCP as the testing has shown this is a good bellwether for the other architectures and cloud providers as well as Cloud Native Hybrids.
|
- We aim to have a "test smart" approach where architectures tested have a good range that can also apply to others. Testing focuses on 10k Omnibus on GCP as the testing has shown this is a good bellwether for the other architectures and cloud providers as well as Cloud Native Hybrids.
|
||||||
- Testing is done publicly and all results are shared.
|
- Testing is done publicly and all results are shared.
|
||||||
|
- For more information about performance testing at GitLab, read [how our QA team leverages GitLab’s performance testing tool (and you can too)](https://about.gitlab.com/blog/2020/02/18/how-were-building-up-performance-testing-of-gitlab/).
|
||||||
|
|
||||||
The following table details the testing done against the reference architectures along with the frequency and results. Additional testing is continuously evaluated, and the table is updated accordingly.
|
The following table details the testing done against the reference architectures along with the frequency and results. Additional testing is continuously evaluated, and the table is updated accordingly.
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ Server hooks run custom logic on the GitLab server. Users can use them to run Gi
|
||||||
- Enforcing specific commit policies.
|
- Enforcing specific commit policies.
|
||||||
- Performing tasks based on the state of the repository.
|
- Performing tasks based on the state of the repository.
|
||||||
|
|
||||||
Server hooks use `pre-receive`, `post-receive`, and `update`
|
Server hooks use `pre-receive`, `post-receive`, and `update`
|
||||||
[Git server-side hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_server_side_hooks).
|
[Git server-side hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_server_side_hooks).
|
||||||
|
|
||||||
GitLab administrators configure server hooks on the file system of the GitLab server. If you don't have file system access,
|
GitLab administrators configure server hooks on the file system of the GitLab server. If you don't have file system access,
|
||||||
|
@ -124,8 +124,8 @@ The following Git environment variables are supported for `pre-receive` and `pos
|
||||||
|:-----------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|:-----------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `GIT_ALTERNATE_OBJECT_DIRECTORIES` | Alternate object directories in the quarantine environment. See [Git `receive-pack` documentation](https://git-scm.com/docs/git-receive-pack#_quarantine_environment). |
|
| `GIT_ALTERNATE_OBJECT_DIRECTORIES` | Alternate object directories in the quarantine environment. See [Git `receive-pack` documentation](https://git-scm.com/docs/git-receive-pack#_quarantine_environment). |
|
||||||
| `GIT_OBJECT_DIRECTORY` | GitLab project path in the quarantine environment. See [Git `receive-pack` documentation](https://git-scm.com/docs/git-receive-pack#_quarantine_environment). |
|
| `GIT_OBJECT_DIRECTORY` | GitLab project path in the quarantine environment. See [Git `receive-pack` documentation](https://git-scm.com/docs/git-receive-pack#_quarantine_environment). |
|
||||||
| `GIT_PUSH_OPTION_COUNT` | Number of push options. See [Git `pre-receive` documentation](https://git-scm.com/docs/githooks#pre-receive). |
|
| `GIT_PUSH_OPTION_COUNT` | Number of [push options](../user/project/push_options.md). See [Git `pre-receive` documentation](https://git-scm.com/docs/githooks#pre-receive). |
|
||||||
| `GIT_PUSH_OPTION_<i>` | Value of push options where `i` is from `0` to `GIT_PUSH_OPTION_COUNT - 1`. See [Git `pre-receive` documentation](https://git-scm.com/docs/githooks#pre-receive). |
|
| `GIT_PUSH_OPTION_<i>` | Value of [push options](../user/project/push_options.md) where `i` is from `0` to `GIT_PUSH_OPTION_COUNT - 1`. See [Git `pre-receive` documentation](https://git-scm.com/docs/githooks#pre-receive). |
|
||||||
|
|
||||||
## Custom error messages
|
## Custom error messages
|
||||||
|
|
||||||
|
|
|
@ -683,6 +683,10 @@ Do not use profanity. Doing so may negatively affect other users and contributor
|
||||||
|
|
||||||
Use lowercase for **push rules**.
|
Use lowercase for **push rules**.
|
||||||
|
|
||||||
|
## register
|
||||||
|
|
||||||
|
Use **register** instead of **sign up** when talking about creating an account.
|
||||||
|
|
||||||
## Reporter
|
## Reporter
|
||||||
|
|
||||||
When writing about the Reporter role:
|
When writing about the Reporter role:
|
||||||
|
@ -780,6 +784,10 @@ Use **sign in** instead of **sign on** or **log on** or **log in**. If the user
|
||||||
|
|
||||||
You can use **single sign-on**.
|
You can use **single sign-on**.
|
||||||
|
|
||||||
|
## sign up
|
||||||
|
|
||||||
|
Use **register** instead of **sign up** when talking about creating an account.
|
||||||
|
|
||||||
## simply, simple
|
## simply, simple
|
||||||
|
|
||||||
Do not use **simply** or **simple**. If the user doesn't find the process to be simple, we lose their trust. ([Vale](../testing.md#vale) rule: [`Simplicity.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/Simplicity.yml))
|
Do not use **simply** or **simple**. If the user doesn't find the process to be simple, we lose their trust. ([Vale](../testing.md#vale) rule: [`Simplicity.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/Simplicity.yml))
|
||||||
|
|
|
@ -567,6 +567,7 @@ sudo -u git -H git config --global gc.auto 0
|
||||||
sudo -u git -H git config --global repack.writeBitmaps true
|
sudo -u git -H git config --global repack.writeBitmaps true
|
||||||
|
|
||||||
# Enable push options
|
# Enable push options
|
||||||
|
# Refer to https://docs.gitlab.com/ee/user/project/push_options.html for more information.
|
||||||
sudo -u git -H git config --global receive.advertisePushOptions true
|
sudo -u git -H git config --global receive.advertisePushOptions true
|
||||||
|
|
||||||
# Enable fsyncObjectFiles to reduce risk of repository corruption if the server crashes
|
# Enable fsyncObjectFiles to reduce risk of repository corruption if the server crashes
|
||||||
|
@ -1143,7 +1144,7 @@ You can configure the Prometheus server in `config/gitlab.yml`:
|
||||||
# example
|
# example
|
||||||
prometheus:
|
prometheus:
|
||||||
enabled: true
|
enabled: true
|
||||||
server_address: '10.1.2.3:9090'
|
server_address: '10.1.2.3:9090'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
|
@ -59,8 +59,10 @@ Otherwise, to upload your license:
|
||||||
1. On the left sidebar, select **Settings**.
|
1. On the left sidebar, select **Settings**.
|
||||||
1. In the **License file** area, select **Upload a license**.
|
1. In the **License file** area, select **Upload a license**.
|
||||||
1. Upload a license:
|
1. Upload a license:
|
||||||
- For a file, select **Upload `.gitlab-license` file**, **Choose file**, and
|
- For a file, either:
|
||||||
select the license file from your local machine.
|
- Select **Upload `.gitlab-license` file**, then **Choose File** and
|
||||||
|
select the license file from your local machine.
|
||||||
|
- Drag and drop the license file to the **Drag your license file here** area.
|
||||||
- For plain text, select **Enter license key** and paste the contents in
|
- For plain text, select **Enter license key** and paste the contents in
|
||||||
**License key**.
|
**License key**.
|
||||||
1. Select the **Terms of Service** checkbox.
|
1. Select the **Terms of Service** checkbox.
|
||||||
|
|
|
@ -137,3 +137,4 @@ For a web developer writing a webpage for your company's website:
|
||||||
- [Suggest code changes](reviews/suggestions.md)
|
- [Suggest code changes](reviews/suggestions.md)
|
||||||
- [Commits](commits.md)
|
- [Commits](commits.md)
|
||||||
- [CI/CD pipelines](../../../ci/index.md)
|
- [CI/CD pipelines](../../../ci/index.md)
|
||||||
|
- [Push options](../push_options.md) for merge requests
|
||||||
|
|
|
@ -57,8 +57,8 @@ GitLab administrators can configure a new default branch name at the
|
||||||
|
|
||||||
### Instance-level custom initial branch name **(FREE SELF)**
|
### Instance-level custom initial branch name **(FREE SELF)**
|
||||||
|
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221013) in GitLab 13.2.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221013) in GitLab 13.2 [with a flag](../../../../administration/feature_flags.md) named `global_default_branch_name`. Enabled by default.
|
||||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/325163) in GitLab 13.12.
|
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/325163) in GitLab 13.12. Feature flag `global_default_branch_name` removed.
|
||||||
|
|
||||||
GitLab [administrators](../../../permissions.md) of self-managed instances can
|
GitLab [administrators](../../../permissions.md) of self-managed instances can
|
||||||
customize the initial branch for projects hosted on that instance. Individual
|
customize the initial branch for projects hosted on that instance. Individual
|
||||||
|
|
|
@ -33,7 +33,7 @@ To fork an existing project in GitLab:
|
||||||
![Choose namespace](img/forking_workflow_choose_namespace_v13_10.png)
|
![Choose namespace](img/forking_workflow_choose_namespace_v13_10.png)
|
||||||
|
|
||||||
- Experimental method. If your GitLab administrator has
|
- Experimental method. If your GitLab administrator has
|
||||||
[enabled the experimental fork project form](#enable-or-disable-the-fork-project-form), read
|
enabled the experimental fork project form, read
|
||||||
[Create a fork with the fork project form](#create-a-fork-with-the-fork-project-form).
|
[Create a fork with the fork project form](#create-a-fork-with-the-fork-project-form).
|
||||||
Only namespaces where you have at least the Developer role for are shown.
|
Only namespaces where you have at least the Developer role for are shown.
|
||||||
|
|
||||||
|
@ -84,14 +84,14 @@ You can unlink your fork from its upstream project in the [advanced settings](..
|
||||||
|
|
||||||
## Create a fork with the fork project form **(FREE SELF)**
|
## Create a fork with the fork project form **(FREE SELF)**
|
||||||
|
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15013) in GitLab 13.11.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15013) in GitLab 13.11 [with a flag](../../../administration/feature_flags.md) named `fork_project_form`. Disabled by default.
|
||||||
> - It's [deployed behind a feature flag](../../../user/feature_flags.md), disabled by default.
|
> - [Enabled on self-managed and GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64967) in GitLab 13.8.
|
||||||
> - It's disabled on GitLab.com.
|
|
||||||
> - It's not recommended for production use.
|
|
||||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-the-fork-project-form). **(FREE SELF)**
|
|
||||||
|
|
||||||
This experimental version of the fork project form is available only if your GitLab
|
FLAG:
|
||||||
administrator has [enabled it](#enable-or-disable-the-fork-project-form):
|
On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../../../administration/feature_flags.md) named `fork_project_form`.
|
||||||
|
On GitLab.com, this feature is available.
|
||||||
|
|
||||||
|
This version of the fork project form is experimental:
|
||||||
|
|
||||||
![Choose namespace](img/fork_form_v13_10.png)
|
![Choose namespace](img/fork_form_v13_10.png)
|
||||||
|
|
||||||
|
@ -102,23 +102,3 @@ To use it, follow the instructions at [Creating a fork](#creating-a-fork) and pr
|
||||||
- The project slug.
|
- The project slug.
|
||||||
- Optional. The project description.
|
- Optional. The project description.
|
||||||
- The visibility level for your fork.
|
- The visibility level for your fork.
|
||||||
|
|
||||||
### Enable or disable the fork project form **(FREE SELF)**
|
|
||||||
|
|
||||||
The new [fork project form](#create-a-fork-with-the-fork-project-form) is under
|
|
||||||
development and not ready for production use. It is deployed behind a feature flag
|
|
||||||
that is **disabled by default**.
|
|
||||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
|
||||||
can enable it.
|
|
||||||
|
|
||||||
To enable it:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Feature.enable(:fork_project_form)
|
|
||||||
```
|
|
||||||
|
|
||||||
To disable it:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Feature.disable(:fork_project_form)
|
|
||||||
```
|
|
||||||
|
|
|
@ -333,3 +333,10 @@ Note that:
|
||||||
|
|
||||||
Behind the scenes, Service Desk works by the special Support Bot user creating issues. This user
|
Behind the scenes, Service Desk works by the special Support Bot user creating issues. This user
|
||||||
does not count toward the license limit count.
|
does not count toward the license limit count.
|
||||||
|
|
||||||
|
## Troubleshooting Service Desk
|
||||||
|
|
||||||
|
### Emails to Service Desk do not create issues
|
||||||
|
|
||||||
|
Your emails might be ignored because they contain one of the
|
||||||
|
[email headers that GitLab ignores](../../administration/incoming_email.md#rejected-headers).
|
||||||
|
|
|
@ -45,6 +45,11 @@ module ContainerRegistry
|
||||||
IMPORT_RESPONSES.fetch(response.status, :error)
|
IMPORT_RESPONSES.fetch(response.status, :error)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def import_status(path)
|
||||||
|
body_hash = response_body(faraday.get(import_url_for(path)))
|
||||||
|
body_hash['status'] || 'error'
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def start_import_for(path, pre:)
|
def start_import_for(path, pre:)
|
||||||
|
|
|
@ -21660,9 +21660,18 @@ msgstr ""
|
||||||
msgid "Licenses|Displays licenses detected in the project, based on the %{linkStart}latest successful%{linkEnd} scan"
|
msgid "Licenses|Displays licenses detected in the project, based on the %{linkStart}latest successful%{linkEnd} scan"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Licenses|Drag your license file here or %{linkStart}click to upload%{linkEnd}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Licenses|Drop your license file to start the upload."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Licenses|Error fetching the license list. Please check your network connection and try again."
|
msgid "Licenses|Error fetching the license list. Please check your network connection and try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Licenses|Error: You are trying to upload something other than a file"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Licenses|License Compliance"
|
msgid "Licenses|License Compliance"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -21681,6 +21690,9 @@ msgstr ""
|
||||||
msgid "Licenses|Specified policies in this project"
|
msgid "Licenses|Specified policies in this project"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Licenses|The file could not be uploaded."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Licenses|The license list details information about the licenses used within your project."
|
msgid "Licenses|The license list details information about the licenses used within your project."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -40731,6 +40743,9 @@ msgstr ""
|
||||||
msgid "Webhooks|An issue is created, updated, closed, or reopened."
|
msgid "Webhooks|An issue is created, updated, closed, or reopened."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Webhooks|Are you sure you want to delete this group hook?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Webhooks|Are you sure you want to delete this project hook?"
|
msgid "Webhooks|Are you sure you want to delete this project hook?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
71
rubocop/cop/gitlab/event_store_subscriber.rb
Normal file
71
rubocop/cop/gitlab/event_store_subscriber.rb
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Gitlab
|
||||||
|
# Cop that checks the implementation of Gitlab::EventStore::Subscriber
|
||||||
|
#
|
||||||
|
# A worker that implements Gitlab::EventStore::Subscriber
|
||||||
|
# must implement the method #handle_event(event) and
|
||||||
|
# must not override the method #perform(*args)
|
||||||
|
#
|
||||||
|
# # bad
|
||||||
|
# class MySubscriber
|
||||||
|
# include Gitlab::EventStore::Subscriber
|
||||||
|
#
|
||||||
|
# def perform(*args)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # bad
|
||||||
|
# class MySubscriber
|
||||||
|
# include Gitlab::EventStore::Subscriber
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # good
|
||||||
|
# class MySubscriber
|
||||||
|
# include Gitlab::EventStore::Subscriber
|
||||||
|
#
|
||||||
|
# def handle_event(event)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
class EventStoreSubscriber < RuboCop::Cop::Cop
|
||||||
|
SUBSCRIBER_MODULE_NAME = 'Gitlab::EventStore::Subscriber'
|
||||||
|
FORBID_PERFORM_OVERRIDE = "Do not override `perform` in a `#{SUBSCRIBER_MODULE_NAME}`."
|
||||||
|
REQUIRE_HANDLE_EVENT = "A `#{SUBSCRIBER_MODULE_NAME}` must implement `#handle_event(event)`."
|
||||||
|
|
||||||
|
def_node_matcher :includes_subscriber?, <<~PATTERN
|
||||||
|
(send nil? :include (const (const (const nil? :Gitlab) :EventStore) :Subscriber))
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
return unless includes_subscriber?(node)
|
||||||
|
|
||||||
|
self.is_subscriber ||= true
|
||||||
|
self.include_subscriber_node ||= node
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_def(node)
|
||||||
|
if is_subscriber && node.method_name == :perform
|
||||||
|
add_offense(node, message: FORBID_PERFORM_OVERRIDE)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.implements_handle_event ||= true if node.method_name == :handle_event
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_investigation_end
|
||||||
|
super
|
||||||
|
|
||||||
|
if is_subscriber && !implements_handle_event
|
||||||
|
add_offense(include_subscriber_node, message: REQUIRE_HANDLE_EVENT)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_accessor :is_subscriber, :include_subscriber_node, :implements_handle_event
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,6 +6,7 @@ exports[`Upload dropzone component correctly overrides description and drop mess
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
||||||
|
@ -86,6 +87,7 @@ exports[`Upload dropzone component when dragging renders correct template when d
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
||||||
|
@ -170,6 +172,7 @@ exports[`Upload dropzone component when dragging renders correct template when d
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
||||||
|
@ -254,6 +257,7 @@ exports[`Upload dropzone component when dragging renders correct template when d
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
||||||
|
@ -339,6 +343,7 @@ exports[`Upload dropzone component when dragging renders correct template when d
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
||||||
|
@ -424,6 +429,7 @@ exports[`Upload dropzone component when dragging renders correct template when d
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
||||||
|
@ -509,6 +515,7 @@ exports[`Upload dropzone component when no slot provided renders default dropzon
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center gl-flex-direction-column"
|
||||||
|
|
|
@ -16,6 +16,7 @@ describe('Upload dropzone component', () => {
|
||||||
const findDropzoneArea = () => wrapper.find('[data-testid="dropzone-area"]');
|
const findDropzoneArea = () => wrapper.find('[data-testid="dropzone-area"]');
|
||||||
const findIcon = () => wrapper.find(GlIcon);
|
const findIcon = () => wrapper.find(GlIcon);
|
||||||
const findUploadText = () => wrapper.find('[data-testid="upload-text"]').text();
|
const findUploadText = () => wrapper.find('[data-testid="upload-text"]').text();
|
||||||
|
const findFileInput = () => wrapper.find('input[type="file"]');
|
||||||
|
|
||||||
function createComponent({ slots = {}, data = {}, props = {} } = {}) {
|
function createComponent({ slots = {}, data = {}, props = {} } = {}) {
|
||||||
wrapper = shallowMount(UploadDropzone, {
|
wrapper = shallowMount(UploadDropzone, {
|
||||||
|
@ -197,4 +198,60 @@ describe('Upload dropzone component', () => {
|
||||||
|
|
||||||
expect(wrapper.element).toMatchSnapshot();
|
expect(wrapper.element).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('file input form name', () => {
|
||||||
|
it('applies inputFieldName as file input name', () => {
|
||||||
|
createComponent({ props: { inputFieldName: 'test_field_name' } });
|
||||||
|
expect(findFileInput().attributes('name')).toBe('test_field_name');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses default file input name if no inputFieldName provided', () => {
|
||||||
|
createComponent();
|
||||||
|
expect(findFileInput().attributes('name')).toBe('upload_file');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updates file input files value', () => {
|
||||||
|
// NOTE: the component assigns dropped files from the drop event to the
|
||||||
|
// input.files property. There's a restriction that nothing but a FileList
|
||||||
|
// can be assigned to this property. While FileList can't be created
|
||||||
|
// manually: it has no constructor. And currently there's no good workaround
|
||||||
|
// for jsdom. So we have to stub the file input in vm.$refs to ensure that
|
||||||
|
// the files property is updated. This enforces following tests to know a
|
||||||
|
// bit too much about the SUT internals See this thread for more details on
|
||||||
|
// FileList in jsdom: https://github.com/jsdom/jsdom/issues/1272
|
||||||
|
function stubFileInputOnWrapper() {
|
||||||
|
const fakeFileInput = { files: [] };
|
||||||
|
wrapper.vm.$refs.fileUpload = fakeFileInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('assigns dragged files to the input files property', async () => {
|
||||||
|
const mockFile = { name: 'test', type: 'image/jpg' };
|
||||||
|
const mockEvent = mockDragEvent({ files: [mockFile] });
|
||||||
|
createComponent({ props: { shouldUpdateInputOnFileDrop: true } });
|
||||||
|
stubFileInputOnWrapper();
|
||||||
|
|
||||||
|
wrapper.trigger('dragenter', mockEvent);
|
||||||
|
await nextTick();
|
||||||
|
wrapper.trigger('drop', mockEvent);
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect(wrapper.vm.$refs.fileUpload.files).toEqual([mockFile]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error when multiple files are dropped on a single file input dropzone', async () => {
|
||||||
|
const mockFile = { name: 'test', type: 'image/jpg' };
|
||||||
|
const mockEvent = mockDragEvent({ files: [mockFile, mockFile] });
|
||||||
|
createComponent({ props: { shouldUpdateInputOnFileDrop: true, singleFileSelection: true } });
|
||||||
|
stubFileInputOnWrapper();
|
||||||
|
|
||||||
|
wrapper.trigger('dragenter', mockEvent);
|
||||||
|
await nextTick();
|
||||||
|
wrapper.trigger('drop', mockEvent);
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect(wrapper.vm.$refs.fileUpload.files).toEqual([]);
|
||||||
|
expect(wrapper.emitted('error')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -77,7 +77,7 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#pre_import_repository' do
|
describe '#import_repository' do
|
||||||
subject { client.import_repository(path) }
|
subject { client.import_repository(path) }
|
||||||
|
|
||||||
where(:status_code, :expected_result) do
|
where(:status_code, :expected_result) do
|
||||||
|
@ -101,6 +101,26 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#import_status' do
|
||||||
|
subject { client.import_status(path) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_import_status(path, status)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a status' do
|
||||||
|
let(:status) { 'this_is_a_test' }
|
||||||
|
|
||||||
|
it { is_expected.to eq(status) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with no status' do
|
||||||
|
let(:status) { nil }
|
||||||
|
|
||||||
|
it { is_expected.to eq('error') }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '.supports_gitlab_api?' do
|
describe '.supports_gitlab_api?' do
|
||||||
subject { described_class.supports_gitlab_api? }
|
subject { described_class.supports_gitlab_api? }
|
||||||
|
|
||||||
|
@ -171,4 +191,14 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
|
||||||
.with(headers: { 'Accept' => described_class::JSON_TYPE })
|
.with(headers: { 'Accept' => described_class::JSON_TYPE })
|
||||||
.to_return(status: status_code, body: '')
|
.to_return(status: status_code, body: '')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def stub_import_status(path, status)
|
||||||
|
stub_request(:get, "#{registry_api_url}/gitlab/v1/import/#{path}/")
|
||||||
|
.with(headers: { 'Accept' => described_class::JSON_TYPE })
|
||||||
|
.to_return(
|
||||||
|
status: 200,
|
||||||
|
body: { status: status }.to_json,
|
||||||
|
headers: { content_type: 'application/json' }
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -218,7 +218,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do
|
||||||
|
|
||||||
subject { repository.finish_pre_import }
|
subject { repository.finish_pre_import }
|
||||||
|
|
||||||
it_behaves_like 'transitioning from allowed states', %w[pre_importing]
|
it_behaves_like 'transitioning from allowed states', %w[pre_importing import_aborted]
|
||||||
|
|
||||||
it 'sets migration_pre_import_done_at' do
|
it 'sets migration_pre_import_done_at' do
|
||||||
expect { subject }.to change { repository.reload.migration_pre_import_done_at }
|
expect { subject }.to change { repository.reload.migration_pre_import_done_at }
|
||||||
|
@ -263,7 +263,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do
|
||||||
|
|
||||||
subject { repository.finish_import }
|
subject { repository.finish_import }
|
||||||
|
|
||||||
it_behaves_like 'transitioning from allowed states', %w[importing]
|
it_behaves_like 'transitioning from allowed states', %w[importing import_aborted]
|
||||||
it_behaves_like 'queueing the next import'
|
it_behaves_like 'queueing the next import'
|
||||||
|
|
||||||
it 'sets migration_import_done_at and queues the next import' do
|
it 'sets migration_import_done_at and queues the next import' do
|
||||||
|
@ -334,46 +334,120 @@ RSpec.describe ContainerRepository, :aggregate_failures do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'transitioning from allowed states', %w[pre_importing]
|
it_behaves_like 'transitioning from allowed states', %w[pre_importing import_aborted]
|
||||||
it_behaves_like 'transitioning to importing'
|
it_behaves_like 'transitioning to importing'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#retry_migration' do
|
describe '#retry_aborted_migration' do
|
||||||
subject { repository.retry_migration }
|
subject { repository.retry_aborted_migration }
|
||||||
|
|
||||||
it 'retries the pre_import' do
|
shared_examples 'no action' do
|
||||||
expect(repository).to receive(:retry_pre_import).and_return(true)
|
it 'does nothing' do
|
||||||
expect(repository).not_to receive(:retry_import)
|
expect { subject }.not_to change { repository.reload.migration_state }
|
||||||
|
|
||||||
expect(subject).to eq(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when migration is done pre-importing' do
|
|
||||||
before do
|
|
||||||
repository.update_columns(migration_pre_import_done_at: Time.zone.now)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns' do
|
|
||||||
expect(repository).to receive(:retry_import).and_return(true)
|
|
||||||
expect(repository).not_to receive(:retry_pre_import)
|
|
||||||
|
|
||||||
expect(subject).to eq(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when migration is already complete' do
|
|
||||||
before do
|
|
||||||
repository.update_columns(migration_import_done_at: Time.zone.now)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns' do
|
|
||||||
expect(repository).not_to receive(:retry_pre_import)
|
|
||||||
expect(repository).not_to receive(:retry_import)
|
|
||||||
|
|
||||||
expect(subject).to eq(nil)
|
expect(subject).to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
shared_examples 'retrying the pre_import' do
|
||||||
|
it 'retries the pre_import' do
|
||||||
|
expect(repository).to receive(:migration_pre_import).and_return(:ok)
|
||||||
|
|
||||||
|
expect { subject }.to change { repository.reload.migration_state }.to('pre_importing')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'retrying the import' do
|
||||||
|
it 'retries the import' do
|
||||||
|
expect(repository).to receive(:migration_import).and_return(:ok)
|
||||||
|
|
||||||
|
expect { subject }.to change { repository.reload.migration_state }.to('importing')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when migration_state is not aborted' do
|
||||||
|
it_behaves_like 'no action'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when migration_state is aborted' do
|
||||||
|
before do
|
||||||
|
repository.abort_import
|
||||||
|
|
||||||
|
allow(repository.gitlab_api_client)
|
||||||
|
.to receive(:import_status).with(repository.path).and_return(client_response)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'native response' do
|
||||||
|
let(:client_response) { 'native' }
|
||||||
|
|
||||||
|
it 'raises an error' do
|
||||||
|
expect { subject }.to raise_error(described_class::NativeImportError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'import_in_progress response' do
|
||||||
|
let(:client_response) { 'import_in_progress' }
|
||||||
|
|
||||||
|
it_behaves_like 'no action'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'import_complete response' do
|
||||||
|
let(:client_response) { 'import_complete' }
|
||||||
|
|
||||||
|
it 'finishes the import' do
|
||||||
|
expect { subject }.to change { repository.reload.migration_state }.to('import_done')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'import_failed response' do
|
||||||
|
let(:client_response) { 'import_failed' }
|
||||||
|
|
||||||
|
it_behaves_like 'retrying the import'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'pre_import_in_progress response' do
|
||||||
|
let(:client_response) { 'pre_import_in_progress' }
|
||||||
|
|
||||||
|
it_behaves_like 'no action'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'pre_import_complete response' do
|
||||||
|
let(:client_response) { 'pre_import_complete' }
|
||||||
|
|
||||||
|
it 'finishes the pre_import and starts the import' do
|
||||||
|
expect(repository).to receive(:finish_pre_import).and_call_original
|
||||||
|
expect(repository).to receive(:migration_import).and_return(:ok)
|
||||||
|
|
||||||
|
expect { subject }.to change { repository.reload.migration_state }.to('importing')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'pre_import_failed response' do
|
||||||
|
let(:client_response) { 'pre_import_failed' }
|
||||||
|
|
||||||
|
it_behaves_like 'retrying the pre_import'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'error response' do
|
||||||
|
let(:client_response) { 'error' }
|
||||||
|
|
||||||
|
context 'migration_pre_import_done_at is NULL' do
|
||||||
|
it_behaves_like 'retrying the pre_import'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'migration_pre_import_done_at is not NULL' do
|
||||||
|
before do
|
||||||
|
repository.update_columns(
|
||||||
|
migration_pre_import_started_at: 5.minutes.ago,
|
||||||
|
migration_pre_import_done_at: Time.zone.now
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'retrying the import'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#tag' do
|
describe '#tag' do
|
||||||
|
|
82
spec/rubocop/cop/gitlab/event_store_subscriber_spec.rb
Normal file
82
spec/rubocop/cop/gitlab/event_store_subscriber_spec.rb
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
|
require_relative '../../../../rubocop/cop/gitlab/event_store_subscriber'
|
||||||
|
|
||||||
|
RSpec.describe RuboCop::Cop::Gitlab::EventStoreSubscriber do
|
||||||
|
subject(:cop) { described_class.new }
|
||||||
|
|
||||||
|
context 'when an event store subscriber overrides #perform' do
|
||||||
|
it 'registers an offense' do
|
||||||
|
expect_offense(<<~WORKER)
|
||||||
|
class SomeWorker
|
||||||
|
include Gitlab::EventStore::Subscriber
|
||||||
|
|
||||||
|
def perform(*args)
|
||||||
|
^^^^^^^^^^^^^^^^^^ Do not override `perform` in a `Gitlab::EventStore::Subscriber`.
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event(event); end
|
||||||
|
end
|
||||||
|
WORKER
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when an event store subscriber does not override #perform' do
|
||||||
|
it 'does not register an offense' do
|
||||||
|
expect_no_offenses(<<~WORKER)
|
||||||
|
class SomeWorker
|
||||||
|
include Gitlab::EventStore::Subscriber
|
||||||
|
|
||||||
|
def handle_event(event); end
|
||||||
|
end
|
||||||
|
WORKER
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when an event store subscriber does not implement #handle_event' do
|
||||||
|
it 'registers an offense' do
|
||||||
|
expect_offense(<<~WORKER)
|
||||||
|
class SomeWorker
|
||||||
|
include Gitlab::EventStore::Subscriber
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A `Gitlab::EventStore::Subscriber` must implement `#handle_event(event)`.
|
||||||
|
end
|
||||||
|
WORKER
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a Sidekiq worker overrides #perform' do
|
||||||
|
it 'does not register an offense' do
|
||||||
|
expect_no_offenses(<<~WORKER)
|
||||||
|
class SomeWorker
|
||||||
|
include ApplicationWorker
|
||||||
|
|
||||||
|
def perform(*args); end
|
||||||
|
end
|
||||||
|
WORKER
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a Sidekiq worker implements #handle_event' do
|
||||||
|
it 'does not register an offense' do
|
||||||
|
expect_no_offenses(<<~WORKER)
|
||||||
|
class SomeWorker
|
||||||
|
include ApplicationWorker
|
||||||
|
|
||||||
|
def handle_event(event); end
|
||||||
|
end
|
||||||
|
WORKER
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'a non worker class' do
|
||||||
|
it 'does not register an offense' do
|
||||||
|
expect_no_offenses(<<~MODEL)
|
||||||
|
class Model < ApplicationRecord
|
||||||
|
include ActiveSupport::Concern
|
||||||
|
end
|
||||||
|
MODEL
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
9
spec/support/cross_database_modification.rb
Normal file
9
spec/support/cross_database_modification.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.after do |example|
|
||||||
|
[::ApplicationRecord, ::Ci::ApplicationRecord].each do |base_class|
|
||||||
|
base_class.gitlab_transactions_stack.clear if base_class.respond_to?(:gitlab_transactions_stack)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -113,6 +113,7 @@ RSpec.describe ContainerRegistry::Migration::EnqueuerWorker, :aggregate_failures
|
||||||
allow(worker).to receive(:next_aborted_repository) do
|
allow(worker).to receive(:next_aborted_repository) do
|
||||||
next_aborted_repository = method.call
|
next_aborted_repository = method.call
|
||||||
allow(next_aborted_repository).to receive(:migration_import).and_return(:ok)
|
allow(next_aborted_repository).to receive(:migration_import).and_return(:ok)
|
||||||
|
allow(next_aborted_repository.gitlab_api_client).to receive(:import_status).and_return('import_failed')
|
||||||
next_aborted_repository
|
next_aborted_repository
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue