Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
79f98200f8
commit
21e144f387
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -2,6 +2,24 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 13.12.3 (2021-06-07)
|
||||
|
||||
### Added (1 change)
|
||||
|
||||
- [Add an option to expose description_html in Release API](gitlab-org/gitlab@47f3fba10dfa82c65b6b006d56cc1724aac411eb) ([merge request](gitlab-org/gitlab!63393))
|
||||
|
||||
### Fixed (5 changes)
|
||||
|
||||
- [Fix spam detection with Akismet client](gitlab-org/gitlab@75dbe8d017ed691d0517f0a6ca7b9bdd866fa9d9) ([merge request](gitlab-org/gitlab!63393))
|
||||
- [Set CSP back to disabled by default](gitlab-org/gitlab@f8f2dbf229693e20171185ae8e31fd59ce2131b3) ([merge request](gitlab-org/gitlab!63393))
|
||||
- [Fix CSP issues related to captchas](gitlab-org/gitlab@cec54814460994ea40311f1091fb7f091d04964f) ([merge request](gitlab-org/gitlab!63393))
|
||||
- [Fix issue with frames not loading in Safari](gitlab-org/gitlab@77b9355f244370b1c184943581f3b6cc27495931) ([merge request](gitlab-org/gitlab!63393))
|
||||
- [Catch PgQuery::ParseError errors and log as-is](gitlab-org/gitlab@a4f36df3701208b5d015e1e818f3d5be3577697a) ([merge request](gitlab-org/gitlab!62795))
|
||||
|
||||
### Changed (1 change)
|
||||
|
||||
- [Improve SSH key expiration warning emails](gitlab-org/gitlab@2e3929503046ab1da5635ef295321ce08843f937) ([merge request](gitlab-org/gitlab!63393))
|
||||
|
||||
## 13.12.1 (2021-05-25)
|
||||
|
||||
### Fixed (3 changes)
|
||||
|
|
|
@ -76,6 +76,7 @@ export default {
|
|||
<gl-sorting
|
||||
:text="sortText"
|
||||
:is-ascending="isDirectionAscending"
|
||||
data-testid="releases-sort"
|
||||
@sortDirectionChange="onDirectionChange"
|
||||
>
|
||||
<gl-sorting-item
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { EDITOR_TYPES } from '~/static_site_editor/rich_content_editor/constants';
|
||||
import RichContentEditor from '~/static_site_editor/rich_content_editor/rich_content_editor.vue';
|
||||
import parseSourceFile from '~/static_site_editor/services/parse_source_file';
|
||||
import { EDITOR_TYPES } from '~/vue_shared/components/rich_content_editor/constants';
|
||||
import RichContentEditor from '~/vue_shared/components/rich_content_editor/rich_content_editor.vue';
|
||||
import imageRepository from '../image_repository';
|
||||
import formatter from '../services/formatter';
|
||||
import renderImage from '../services/renderers/render_image';
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
- paginatable = local_assigns.fetch(:paginatable, false)
|
||||
- provider_title = Gitlab::ImportSources.title(provider)
|
||||
|
||||
- header_title _("New project"), new_project_path
|
||||
- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
|
||||
|
||||
#import-projects-mount-element{ data: { provider: provider, provider_title: provider_title,
|
||||
can_select_namespace: current_user.can_select_namespace?.to_s,
|
||||
ci_cd_only: has_ci_cd_only_params?.to_s,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
- title = _('Bitbucket Server Import')
|
||||
- page_title title
|
||||
- breadcrumb_title title
|
||||
- header_title _("Projects"), root_path
|
||||
- page_title _('Bitbucket Server Import')
|
||||
- header_title _("New project"), new_project_path
|
||||
- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
|
||||
|
||||
%h3.page-title.d-flex
|
||||
.gl-display-flex.gl-align-items-center.gl-justify-content-center
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title _('Bitbucket Server import')
|
||||
- header_title _('Projects'), root_path
|
||||
|
||||
%h3.page-title.d-flex
|
||||
.gl-display-flex.gl-align-items-center.gl-justify-content-center
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
- page_title _("FogBugz Import")
|
||||
- header_title _("Projects"), root_path
|
||||
- header_title _("New project"), new_project_path
|
||||
- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
|
||||
|
||||
%h3.page-title.d-flex
|
||||
.gl-display-flex.gl-align-items-center.gl-justify-content-center
|
||||
= sprite_icon('bug', css_class: 'gl-mr-2')
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
- page_title _('User map'), _('FogBugz import')
|
||||
- header_title _("Projects"), root_path
|
||||
- header_title _("New project"), new_project_path
|
||||
- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
|
||||
|
||||
%h3.page-title.d-flex
|
||||
.gl-display-flex.gl-align-items-center.gl-justify-content-center
|
||||
= sprite_icon('bug', css_class: 'gl-mr-2')
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title _("FogBugz import")
|
||||
- header_title _("Projects"), root_path
|
||||
%h3.page-title.d-flex
|
||||
.gl-display-flex.gl-align-items-center.gl-justify-content-center
|
||||
= sprite_icon('bug', css_class: 'gl-mr-2')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
- page_title _("Gitea Import")
|
||||
- header_title _("Projects"), root_path
|
||||
- header_title _("New project"), new_project_path
|
||||
- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
|
||||
|
||||
%h3.page-title
|
||||
= custom_icon('gitea_logo')
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title _("Gitea Import")
|
||||
- header_title _("Projects"), root_path
|
||||
%h3.page-title
|
||||
= custom_icon('gitea_logo')
|
||||
= _('Import Projects from Gitea')
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
- title = _('Authenticate with GitHub')
|
||||
- page_title title
|
||||
- breadcrumb_title title
|
||||
- header_title _("Projects"), root_path
|
||||
- header_title _("New project"), new_project_path
|
||||
- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
|
||||
|
||||
%h2.page-title
|
||||
%h3.page-title
|
||||
= title
|
||||
|
||||
%p
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
- title = has_ci_cd_only_params? ? _('Connect repositories from GitHub') : _('GitHub import')
|
||||
- page_title title
|
||||
- breadcrumb_title title
|
||||
- header_title _("Projects"), root_path
|
||||
%h3.page-title.mb-0.gl-display-flex
|
||||
.gl-display-flex.gl-align-items-center.gl-justify-content-center
|
||||
= sprite_icon('github', css_class: 'gl-mr-2')
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title _("GitLab.com import")
|
||||
- header_title _("Projects"), root_path
|
||||
%h3.page-title
|
||||
= sprite_icon('heart', css_class: 'gl-vertical-align-middle')
|
||||
= _('Import projects from GitLab.com')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
- page_title _("GitLab Import")
|
||||
- header_title _("Projects"), root_path
|
||||
- header_title _("New project"), new_project_path
|
||||
- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
|
||||
|
||||
%h3.page-title.d-flex
|
||||
.gl-display-flex.gl-align-items-center.gl-justify-content-center
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
- page_title _("Manifest file import")
|
||||
- header_title _("Projects"), root_path
|
||||
- header_title _("New project"), new_project_path
|
||||
- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
|
||||
|
||||
|
||||
%h3.page-title
|
||||
= _('Manifest file import')
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- page_title _("Manifest import")
|
||||
- header_title _("Projects"), root_path
|
||||
|
||||
%h3.page-title
|
||||
= _('Manifest file import')
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
- title = _('Phabricator Server Import')
|
||||
- page_title title
|
||||
- breadcrumb_title title
|
||||
- header_title _("Projects"), root_path
|
||||
- page_title _('Phabricator Server Import')
|
||||
- header_title _("New project"), new_project_path
|
||||
- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
|
||||
|
||||
%h3.page-title.d-flex
|
||||
.gl-display-flex.gl-align-items-center.gl-justify-content-center
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
#{job.coverage}%
|
||||
|
||||
%td
|
||||
.gl-display-flex
|
||||
.gl-text-right
|
||||
.btn-group
|
||||
- if can?(current_user, :read_job_artifacts, job) && job.artifacts?
|
||||
= link_to download_project_job_artifacts_path(job.project, job), rel: 'nofollow', download: '', title: _('Download artifacts'), class: 'gl-button btn btn-default btn-icon' do
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
= s_('Jobs|Use jobs to automate your tasks')
|
||||
%p
|
||||
= s_('Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project.')
|
||||
= link_to s_('Jobs|Create CI/CD configuration file'), project_ci_pipeline_editor_path(project), class: 'btn gl-button btn-info js-empty-state-button'
|
||||
= link_to s_('Jobs|Create CI/CD configuration file'), project_ci_pipeline_editor_path(project), class: 'btn gl-button btn-confirm js-empty-state-button'
|
||||
- else
|
||||
.nothing-here-block= s_('Jobs|No jobs to show')
|
||||
- else
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# # frozen_string_literal: true
|
||||
|
||||
if Gitlab::Utils.to_boolean(ENV['ENABLE_ACTIVERECORD_EMPTY_PING'], default: true)
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Database::PostgresqlAdapter::EmptyQueryPing)
|
||||
end
|
||||
|
||||
if Gitlab::Utils.to_boolean(ENV['ENABLE_ACTIVERECORD_TYPEMAP_CACHE'], default: false)
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Database::PostgresqlAdapter::TypeMapCache)
|
||||
end
|
|
@ -180,7 +180,7 @@ successfully, you must replicate their data using some other means.
|
|||
|[Project wiki repository](../../../user/project/wiki/) | **Yes** (10.2) | **Yes** (10.7) | No | |
|
||||
|[Group wiki repository](../../../user/project/wiki/index.md#group-wikis) | [**Yes** (13.10)](https://gitlab.com/gitlab-org/gitlab/-/issues/208147) | No | No | Behind feature flag `geo_group_wiki_repository_replication`, enabled by default. |
|
||||
|[Uploads](../../uploads.md) | **Yes** (10.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | No | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both sites and comparing the output between them. |
|
||||
|[LFS objects](../../lfs/index.md) | **Yes** (10.2) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/8922) | Via Object Storage provider if supported. Native Geo support (Beta). | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both sites and comparing the output between them. GitLab versions 11.11.x and 12.0.x are affected by [a bug that prevents any new LFS objects from replicating](https://gitlab.com/gitlab-org/gitlab/-/issues/32696). |
|
||||
|[LFS objects](../../lfs/index.md) | **Yes** (10.2) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/8922) | Via Object Storage provider if supported. Native Geo support (Beta). | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both sites and comparing the output between them. GitLab versions 11.11.x and 12.0.x are affected by [a bug that prevents any new LFS objects from replicating](https://gitlab.com/gitlab-org/gitlab/-/issues/32696).<br /><br />Behind feature flag `geo_lfs_object_replication`, enabled by default. |
|
||||
|[Personal snippets](../../../user/snippets.md) | **Yes** (10.2) | **Yes** (10.2) | No | |
|
||||
|[Project snippets](../../../user/snippets.md) | **Yes** (10.2) | **Yes** (10.2) | No | |
|
||||
|[CI job artifacts (other than Job Logs)](../../../ci/pipelines/job_artifacts.md) | **Yes** (10.4) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/8923) | Via Object Storage provider if supported. Native Geo support (Beta). | Verified only manually using [Integrity Check Rake Task](../../raketasks/check.md) on both sites and comparing the output between them. |
|
||||
|
@ -205,33 +205,3 @@ successfully, you must replicate their data using some other means.
|
|||
|[GitLab Pages](../../pages/index.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/589) | No | No | |
|
||||
|[Dependency proxy images](../../../user/packages/dependency_proxy/index.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/259694) | No | No | Blocked on [Geo: Secondary Mimicry](https://gitlab.com/groups/gitlab-org/-/epics/1528). Note that replication of this cache is not needed for Disaster Recovery purposes because it can be recreated from external sources. |
|
||||
|[Vulnerability Export](../../../user/application_security/vulnerability_report/#export-vulnerability-details) | [Not planned](https://gitlab.com/groups/gitlab-org/-/epics/3111) | No | Via Object Storage provider if supported. Native Geo support (Beta). | Not planned because they are ephemeral and sensitive. They can be regenerated on demand. |
|
||||
|
||||
#### LFS object replication using the self service framework
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276696) in GitLab 13.12.
|
||||
> - [Deployed behind a feature flag](../../../user/feature_flags.md), enabled by default.
|
||||
> - Not enabled on GitLab.com as Geo is not enabled.
|
||||
> - Recommended for production use.
|
||||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-lfs-object-replication-using-the-self-service-framework).
|
||||
|
||||
There can be [risks when disabling released features](../../../user/feature_flags.md#risks-when-disabling-released-features).
|
||||
Refer to this feature's version history for more details.
|
||||
|
||||
##### Enable or disable LFS object replication using the self service framework
|
||||
|
||||
LFS object replication using the self service framework is under development but ready for production use. It is
|
||||
deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can opt to disable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:geo_lfs_object_replication)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:geo_lfs_object_replication)
|
||||
```
|
||||
|
|
|
@ -246,7 +246,7 @@ control over how the Pages daemon runs and serves content in your environment.
|
|||
| `gitlab_retrieval_timeout` | The maximum time to wait for a response from the GitLab API per request (default: 30s). |
|
||||
| `gitlab_retrieval_interval` | The interval to wait before retrying to resolve a domain's configuration via the GitLab API (default: 1s). |
|
||||
| `gitlab_retrieval_retries` | The maximum number of times to retry to resolve a domain's configuration via the API (default: 3). |
|
||||
| `domain_config_source` | Domain configuration source (default: `auto`) |
|
||||
| `domain_config_source` | This parameter was removed in 14.0, on earlier versions it can be used to enable and test API domain configuration source |
|
||||
| `gitlab_id` | The OAuth application public ID. Leave blank to automatically fill when Pages authenticates with GitLab. |
|
||||
| `gitlab_secret` | The OAuth application secret. Leave blank to automatically fill when Pages authenticates with GitLab. |
|
||||
| `auth_scope` | The OAuth application scope to use for authentication. Must match GitLab Pages OAuth application settings. Leave blank to use `api` scope by default. |
|
||||
|
@ -281,6 +281,7 @@ control over how the Pages daemon runs and serves content in your environment.
|
|||
| **`pages_nginx[]`** | |
|
||||
| `enable` | Include a virtual host `server{}` block for Pages inside NGINX. Needed for NGINX to proxy traffic back to the Pages daemon. Set to `false` if the Pages daemon should directly receive all requests, for example, when using [custom domains](index.md#custom-domains). |
|
||||
| `FF_ENABLE_REDIRECTS` | Feature flag to disable redirects (enabled by default). Read the [redirects documentation](../../user/project/pages/redirects.md#disable-redirects) for more information. |
|
||||
| `use_legacy_storage` | Temporarily-introduced parameter allowing to use legacy domain configuration source and storage. [Will be removed in GitLab 14.3](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6166). |
|
||||
|
||||
---
|
||||
|
||||
|
@ -756,51 +757,37 @@ Pages server.
|
|||
|
||||
## Domain source configuration
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217912) in GitLab 13.3.
|
||||
When GitLab Pages daemon serves pages requests it firstly needs to identify which project should be used to
|
||||
serve the requested URL and how its content is stored.
|
||||
|
||||
GitLab Pages can use different sources to get domain configuration.
|
||||
The default value for Omnibus installations is `nil`.
|
||||
Before GitLab 13.3, all pages content was extracted to the special shared directory,
|
||||
and each project had a special configuration file.
|
||||
The Pages daemon was reading these configuration files and storing their content in memory.
|
||||
|
||||
```ruby
|
||||
gitlab_pages['domain_config_source'] = nil
|
||||
```
|
||||
This approach had several disadvantages and was replaced with GitLab Pages using the internal GitLab API
|
||||
every time a new domain is requested.
|
||||
The domain information is also cached by the Pages daemon to speed up subsequent requests.
|
||||
|
||||
If left unchanged, GitLab Pages tries to use any available source (either `gitlab` or `disk`). The
|
||||
preferred source is `gitlab`, which uses [API-based configuration](#gitlab-api-based-configuration).
|
||||
From [GitLab 13.3 to GitLab 13.12](#domain-source-configuration-before-140) GitLab Pages supported both ways of obtaining domain information.
|
||||
|
||||
On large GitLab instances, using the API-based configuration significantly improves the pages daemon startup time, as there is no need to load all custom domains configuration into memory.
|
||||
Starting from [GitLab 14.0](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5993) GitLab Pages uses API
|
||||
by default and fails to start if it can't connect to it.
|
||||
For common issues, see the [troubleshooting section](#failed-to-connect-to-the-internal-gitlab-api).
|
||||
|
||||
For more details see this [blog post](https://about.gitlab.com/blog/2020/08/03/how-gitlab-pages-uses-the-gitlab-api-to-serve-content/).
|
||||
|
||||
### Deprecated `domain_config_source`
|
||||
### Domain source configuration before 14.0
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217912) in GitLab 13.3.
|
||||
|
||||
WARNING:
|
||||
The flag `gitlab_pages['domain_config_source']` is deprecated for use in [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/217913),
|
||||
and is planned for removal in GitLab 14.0.
|
||||
`domain_config_source` parameter is removed and has no effect starting from [GitLab 14.0](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5993)
|
||||
|
||||
GitLab 13.0 introduced the special flag `domain_config_source` to support manual opt-in to
|
||||
[API-based configuration](#gitlab-api-based-configuration).
|
||||
GitLab 13.7 introduced the [`auto` value](https://gitlab.com/gitlab-org/gitlab/-/issues/218358)
|
||||
to support a smoother transition to API-based configuration.
|
||||
From [GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/issues/217912) to [GitLab 13.12](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5993) GitLab Pages can either use `disk` or `gitlab` domain configuration source.
|
||||
|
||||
Starting with GitLab 14.0, GitLab Pages only supports API-based configuration, and
|
||||
[disk source configuration is removed](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/382).
|
||||
Therefore, GitLab 14.0 also removes `domain_config_source`.
|
||||
We highly advise you to use `gitlab` configuration source as it will make transition to newer versions easier.
|
||||
|
||||
GitLab Pages fails to start if it can't connect to the GitLab API. For other common issues, see the
|
||||
[troubleshooting section](#failed-to-connect-to-the-internal-gitlab-api)
|
||||
or report an issue.
|
||||
|
||||
### GitLab API-based configuration
|
||||
|
||||
WARNING:
|
||||
The flag `gitlab_pages['domain_config_source']` is deprecated for use in [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/217913),
|
||||
and is planned for removal in GitLab 14.0. In GitLab 14.0 and later, GitLab Pages attempts to
|
||||
connect to the API automatically, without requiring the manual configuration steps shown here. Pages
|
||||
fails to start if this automatic connection fails.
|
||||
|
||||
GitLab Pages can use an API-based configuration. This replaces disk source configuration, which
|
||||
was used prior to GitLab 13.0. Follow these steps to enable it:
|
||||
To explicitly enable API source:
|
||||
|
||||
1. Add the following to your `/etc/gitlab/gitlab.rb` file:
|
||||
|
||||
|
@ -810,14 +797,15 @@ was used prior to GitLab 13.0. Follow these steps to enable it:
|
|||
|
||||
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
|
||||
If you encounter an issue, you can disable it by choosing `disk`:
|
||||
Or if you want to use legacy confiration source you can:
|
||||
|
||||
```ruby
|
||||
gitlab_pages['domain_config_source'] = "disk"
|
||||
```
|
||||
1. Add the following to your `/etc/gitlab/gitlab.rb` file:
|
||||
|
||||
For other common issues, see the [troubleshooting section](#failed-to-connect-to-the-internal-gitlab-api)
|
||||
or report an issue.
|
||||
```ruby
|
||||
gitlab_pages['domain_config_source'] = "disk"
|
||||
```
|
||||
|
||||
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
|
||||
### GitLab API cache configuration
|
||||
|
||||
|
@ -1052,7 +1040,7 @@ To migrate GitLab Pages to GitLab 14.0:
|
|||
1. If your current GitLab version is lower than 13.12, then you first need to upgrade to 13.12.
|
||||
Upgrading directly to 14.0 may cause downtime for some web-sites hosted on GitLab Pages
|
||||
until you finish the following steps.
|
||||
1. Enable the [API-based configuration](#gitlab-api-based-configuration), which
|
||||
1. Set [`domain_config_source` to `gitlab`](#domain-source-configuration-before-140), which
|
||||
is the default starting from GitLab 14.0. Skip this step if you're already running GitLab 14.0 or above.
|
||||
1. If you want to store your pages content in the [object storage](#using-object-storage), make sure to configure it.
|
||||
If you want to store the pages content locally or continue using an NFS server, skip this step.
|
||||
|
@ -1082,6 +1070,16 @@ but commented out to help encourage others to add to it in the future. -->
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
### How to see GitLab Pages logs
|
||||
|
||||
You can see Pages daemon logs by running:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl tail gitlab-pages
|
||||
```
|
||||
|
||||
You can also find the log file in `/var/log/gitlab/gitlab-pages/current`.
|
||||
|
||||
### `open /etc/ssl/ca-bundle.pem: permission denied`
|
||||
|
||||
GitLab Pages runs inside a `chroot` jail, usually in a uniquely numbered directory like
|
||||
|
@ -1216,7 +1214,7 @@ Alternatively, run the CI pipelines of those projects that contain a `pages` job
|
|||
|
||||
### Failed to connect to the internal GitLab API
|
||||
|
||||
If you have enabled [API-based configuration](#gitlab-api-based-configuration) and see the following error:
|
||||
If you see the following error:
|
||||
|
||||
```plaintext
|
||||
ERRO[0010] Failed to connect to the internal GitLab API after 0.50s error="failed to connect to internal Pages API: HTTP status: 401"
|
||||
|
@ -1237,11 +1235,6 @@ error="failed to connect to internal Pages API: Get \"https://gitlab.example.com
|
|||
|
||||
### Pages cannot communicate with an instance of the GitLab API
|
||||
|
||||
WARNING:
|
||||
The flag `gitlab_pages['domain_config_source']` is [deprecated](#deprecated-domain_config_source)
|
||||
for use in [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/217913),
|
||||
and is planned for removal in GitLab 14.0.
|
||||
|
||||
If you use the default value for `domain_config_source=auto` and run multiple instances of GitLab
|
||||
Pages, you may see intermittent 502 error responses while serving Pages content. You may also see
|
||||
the following warning in the Pages logs:
|
||||
|
@ -1322,3 +1315,25 @@ To enable disk access:
|
|||
```
|
||||
|
||||
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||
|
||||
### GitLab Pages doesn't work after upgrading to GitLab 14.0 or above
|
||||
|
||||
GitLab 14.0 introduces a number of changes to GitLab Pages which may require manual intervention.
|
||||
|
||||
1. Firstly [follow the migration guide](#migrate-gitlab-pages-to-140).
|
||||
1. If it doesn't work, see [GitLab Pages logs](#how-to-see-gitlab-pages-logs), and if you see any errors there then search them on this page.
|
||||
|
||||
WARNING:
|
||||
As the last resort you can temporarily enable legacy storage and configuration mechanisms. Support for them [will be removed in GitLab 14.3](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6166), so GitLab Pages will stop working if don't resolve the underlying issue.
|
||||
|
||||
To do that:
|
||||
|
||||
1. Please describe the issue you're seeing in [here](https://gitlab.com/gitlab-org/gitlab/-/issues/331699).
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
gitlab_pages['use_legacy_storage'] = true
|
||||
```
|
||||
|
||||
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||
|
|
|
@ -250,7 +250,8 @@ Example response:
|
|||
"path_with_namespace": "diaspora/diaspora-project-site",
|
||||
"created_at": "2018-07-03T05:48:49.982Z",
|
||||
"default_branch": null,
|
||||
"tag_list": [],
|
||||
"tag_list": [], //deprecated, use `topics` instead
|
||||
"topics": [],
|
||||
"ssh_url_to_repo": "ssh://user@example.com/diaspora/diaspora-project-site.git",
|
||||
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-project-site",
|
||||
|
|
|
@ -302,7 +302,8 @@ Example response:
|
|||
"id": 9,
|
||||
"description": "foo",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"tag_list": [], //deprecated, use `topics` instead
|
||||
"topics": [],
|
||||
"archived": false,
|
||||
"visibility": "internal",
|
||||
"ssh_url_to_repo": "git@gitlab.example.com/html5-boilerplate.git",
|
||||
|
@ -381,9 +382,8 @@ Example response:
|
|||
"path_with_namespace":"h5bp/html5-boilerplate",
|
||||
"created_at":"2020-04-27T06:13:22.642Z",
|
||||
"default_branch":"master",
|
||||
"tag_list":[
|
||||
|
||||
],
|
||||
"tag_list":[], //deprecated, use `topics` instead
|
||||
"topics":[],
|
||||
"ssh_url_to_repo":"ssh://git@gitlab.com/h5bp/html5-boilerplate.git",
|
||||
"http_url_to_repo":"http://gitlab.com/h5bp/html5-boilerplate.git",
|
||||
"web_url":"http://gitlab.com/h5bp/html5-boilerplate",
|
||||
|
@ -540,7 +540,8 @@ Example response:
|
|||
"id": 7,
|
||||
"description": "Voluptas veniam qui et beatae voluptas doloremque explicabo facilis.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"tag_list": [], //deprecated, use `topics` instead
|
||||
"topics": [],
|
||||
"archived": false,
|
||||
"visibility": "public",
|
||||
"ssh_url_to_repo": "git@gitlab.example.com:twitter/typeahead-js.git",
|
||||
|
@ -578,7 +579,8 @@ Example response:
|
|||
"id": 6,
|
||||
"description": "Aspernatur omnis repudiandae qui voluptatibus eaque.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"tag_list": [], //deprecated, use `topics` instead
|
||||
"topics": [],
|
||||
"archived": false,
|
||||
"visibility": "internal",
|
||||
"ssh_url_to_repo": "git@gitlab.example.com:twitter/flight.git",
|
||||
|
@ -618,7 +620,8 @@ Example response:
|
|||
"id": 8,
|
||||
"description": "Velit eveniet provident fugiat saepe eligendi autem.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"tag_list": [], //deprecated, use `topics` instead
|
||||
"topics": [],
|
||||
"archived": false,
|
||||
"visibility": "private",
|
||||
"ssh_url_to_repo": "git@gitlab.example.com:h5bp/html5-boilerplate.git",
|
||||
|
@ -886,7 +889,8 @@ Example response:
|
|||
"id": 9,
|
||||
"description": "foo",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"tag_list": [], //deprecated, use `topics` instead
|
||||
"topics": [],
|
||||
"public": false,
|
||||
"archived": false,
|
||||
"visibility": "internal",
|
||||
|
|
|
@ -151,7 +151,8 @@ Example response:
|
|||
"path_with_namespace":"root/project-with-clusters-api",
|
||||
"created_at":"2019-01-02T20:13:32.600Z",
|
||||
"default_branch":null,
|
||||
"tag_list":[],
|
||||
"tag_list":[], //deprecated, use `topics` instead
|
||||
"topics":[],
|
||||
"ssh_url_to_repo":"ssh://gitlab.example.com/root/project-with-clusters-api.git",
|
||||
"http_url_to_repo":"https://gitlab.example.com/root/project-with-clusters-api.git",
|
||||
"web_url":"https://gitlab.example.com/root/project-with-clusters-api",
|
||||
|
@ -247,7 +248,8 @@ Example response:
|
|||
"path_with_namespace":"root/project-with-clusters-api",
|
||||
"created_at":"2019-01-02T20:13:32.600Z",
|
||||
"default_branch":null,
|
||||
"tag_list":[],
|
||||
"tag_list":[], //deprecated, use `topics` instead
|
||||
"topics":[],
|
||||
"ssh_url_to_repo":"ssh:://gitlab.example.com/root/project-with-clusters-api.git",
|
||||
"http_url_to_repo":"https://gitlab.example.com/root/project-with-clusters-api.git",
|
||||
"web_url":"https://gitlab.example.com/root/project-with-clusters-api",
|
||||
|
@ -357,7 +359,8 @@ Example response:
|
|||
"path_with_namespace":"root/project-with-clusters-api",
|
||||
"created_at":"2019-01-02T20:13:32.600Z",
|
||||
"default_branch":null,
|
||||
"tag_list":[],
|
||||
"tag_list":[], //deprecated, use `topics` instead
|
||||
"topics":[],
|
||||
"ssh_url_to_repo":"ssh:://gitlab.example.com/root/project-with-clusters-api.git",
|
||||
"http_url_to_repo":"https://gitlab.example.com/root/project-with-clusters-api.git",
|
||||
"web_url":"https://gitlab.example.com/root/project-with-clusters-api",
|
||||
|
|
|
@ -59,7 +59,7 @@ GET /projects
|
|||
| `sort` | string | **{dotted-circle}** No | Return projects sorted in `asc` or `desc` order. Default is `desc`. |
|
||||
| `starred` | boolean | **{dotted-circle}** No | Limit by projects starred by the current user. |
|
||||
| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. |
|
||||
| `topic` | string | **{dotted-circle}** No | Comma-separated topic names. Limit results to projects that match all of given topics. See `tag_list` attribute. |
|
||||
| `topic` | string | **{dotted-circle}** No | Comma-separated topic names. Limit results to projects that match all of given topics. See `topics` attribute. |
|
||||
| `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. |
|
||||
| `wiki_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2). |
|
||||
| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
|
||||
|
@ -82,7 +82,11 @@ When `simple=true` or the user is unauthenticated this returns something like:
|
|||
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-client",
|
||||
"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"disapora client"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"disapora client"
|
||||
],
|
||||
|
@ -116,7 +120,11 @@ When the user is authenticated and `simple` is not set this returns something li
|
|||
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-client",
|
||||
"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"disapora client"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"disapora client"
|
||||
],
|
||||
|
@ -200,7 +208,11 @@ When the user is authenticated and `simple` is not set this returns something li
|
|||
"http_url_to_repo": "http://example.com/brightbox/puppet.git",
|
||||
"web_url": "http://example.com/brightbox/puppet",
|
||||
"readme_url": "http://example.com/brightbox/puppet/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"puppet"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"puppet"
|
||||
],
|
||||
|
@ -300,6 +312,10 @@ When the user is authenticated and `simple` is not set this returns something li
|
|||
]
|
||||
```
|
||||
|
||||
NOTE:
|
||||
The `tag_list` attribute has been deprecated
|
||||
and is removed in API v5 in favor of the `topics` attribute.
|
||||
|
||||
NOTE:
|
||||
For users of [GitLab Premium or higher](https://about.gitlab.com/pricing/),
|
||||
the `marked_for_deletion_at` attribute has been deprecated, and is removed
|
||||
|
@ -378,7 +394,11 @@ GET /users/:user_id/projects
|
|||
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-client",
|
||||
"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"disapora client"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"disapora client"
|
||||
],
|
||||
|
@ -462,7 +482,11 @@ GET /users/:user_id/projects
|
|||
"http_url_to_repo": "http://example.com/brightbox/puppet.git",
|
||||
"web_url": "http://example.com/brightbox/puppet",
|
||||
"readme_url": "http://example.com/brightbox/puppet/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"puppet"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"puppet"
|
||||
],
|
||||
|
@ -606,7 +630,11 @@ Example response:
|
|||
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-client",
|
||||
"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"disapora client"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"disapora client"
|
||||
],
|
||||
|
@ -683,7 +711,11 @@ Example response:
|
|||
"http_url_to_repo": "http://example.com/brightbox/puppet.git",
|
||||
"web_url": "http://example.com/brightbox/puppet",
|
||||
"readme_url": "http://example.com/brightbox/puppet/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"puppet"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"puppet"
|
||||
],
|
||||
|
@ -804,7 +836,11 @@ GET /projects/:id
|
|||
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-project-site",
|
||||
"readme_url": "http://example.com/diaspora/diaspora-project-site/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
|
@ -940,6 +976,10 @@ GET /projects/:id
|
|||
}
|
||||
```
|
||||
|
||||
NOTE:
|
||||
The `tag_list` attribute has been deprecated
|
||||
and is removed in API v5 in favor of the `topics` attribute.
|
||||
|
||||
Users of [GitLab Premium or higher](https://about.gitlab.com/pricing/)
|
||||
can also see the `approvals_before_merge` parameter:
|
||||
|
||||
|
@ -974,7 +1014,8 @@ If the project is a fork, and you provide a valid token to authenticate, the
|
|||
"path_with_namespace":"gitlab-org/gitlab-foss",
|
||||
"created_at":"2013-09-26T06:02:36.000Z",
|
||||
"default_branch":"master",
|
||||
"tag_list":[],
|
||||
"tag_list":[], //deprecated, use `topics` instead
|
||||
"topics":[],
|
||||
"ssh_url_to_repo":"git@gitlab.com:gitlab-org/gitlab-foss.git",
|
||||
"http_url_to_repo":"https://gitlab.com/gitlab-org/gitlab-foss.git",
|
||||
"web_url":"https://gitlab.com/gitlab-org/gitlab-foss",
|
||||
|
@ -1393,7 +1434,11 @@ Example responses:
|
|||
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-project-site",
|
||||
"readme_url": "http://example.com/diaspora/diaspora-project-site/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
|
@ -1480,7 +1525,11 @@ Example response:
|
|||
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-project-site",
|
||||
"readme_url": "http://example.com/diaspora/diaspora-project-site/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
|
@ -1573,7 +1622,11 @@ Example response:
|
|||
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-project-site",
|
||||
"readme_url": "http://example.com/diaspora/diaspora-project-site/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
|
@ -1741,7 +1794,11 @@ Example response:
|
|||
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-project-site",
|
||||
"readme_url": "http://example.com/diaspora/diaspora-project-site/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
|
@ -1855,7 +1912,11 @@ Example response:
|
|||
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
|
||||
"web_url": "http://example.com/diaspora/diaspora-project-site",
|
||||
"readme_url": "http://example.com/diaspora/diaspora-project-site/blob/master/README.md",
|
||||
"tag_list": [
|
||||
"tag_list": [ //deprecated, use `topics` instead
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
"topics": [
|
||||
"example",
|
||||
"disapora project"
|
||||
],
|
||||
|
@ -2441,7 +2502,8 @@ Example response:
|
|||
"path_with_namespace": "cute-cats/hello-world",
|
||||
"created_at": "2020-10-15T16:25:22.415Z",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"tag_list": [], //deprecated, use `topics` instead
|
||||
"topics": [],
|
||||
"ssh_url_to_repo": "git@gitlab.example.com:cute-cats/hello-world.git",
|
||||
"http_url_to_repo": "https://gitlab.example.com/cute-cats/hello-world.git",
|
||||
"web_url": "https://gitlab.example.com/cute-cats/hello-world",
|
||||
|
|
|
@ -54,7 +54,8 @@ Example response:
|
|||
"path_with_namespace": "twitter/flight",
|
||||
"created_at": "2017-09-05T07:58:01.621Z",
|
||||
"default_branch": "master",
|
||||
"tag_list":[],
|
||||
"tag_list":[], //deprecated, use `topics` instead
|
||||
"topics":[],
|
||||
"ssh_url_to_repo": "ssh://jarka@localhost:2222/twitter/flight.git",
|
||||
"http_url_to_repo": "http://localhost:3000/twitter/flight.git",
|
||||
"web_url": "http://localhost:3000/twitter/flight",
|
||||
|
@ -475,7 +476,8 @@ Example response:
|
|||
"path_with_namespace": "twitter/flight",
|
||||
"created_at": "2017-09-05T07:58:01.621Z",
|
||||
"default_branch": "master",
|
||||
"tag_list":[],
|
||||
"tag_list":[], //deprecated, use `topics` instead
|
||||
"topics":[],
|
||||
"ssh_url_to_repo": "ssh://jarka@localhost:2222/twitter/flight.git",
|
||||
"http_url_to_repo": "http://localhost:3000/twitter/flight.git",
|
||||
"web_url": "http://localhost:3000/twitter/flight",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: package
|
||||
group: package
|
||||
stage: Package
|
||||
group: Package
|
||||
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/#designated-technical-writers
|
||||
comments: false
|
||||
description: 'Container Registry metadata database'
|
||||
|
|
|
@ -7174,6 +7174,18 @@ Status: `data_available`
|
|||
|
||||
Tiers: `free`
|
||||
|
||||
### `license_billable_users`
|
||||
|
||||
Number of all billable users (active users excluding bots and guests).
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/license/20210531204603_license_billable_users.yml)
|
||||
|
||||
Group: `group::product intelligence`
|
||||
|
||||
Status: `implemented`
|
||||
|
||||
Tiers: `premium`, `ultimate`
|
||||
|
||||
### `license_expires_at`
|
||||
|
||||
The date the license ends
|
||||
|
|
|
@ -175,7 +175,7 @@ You can [configure](#customizing-the-container-scanning-settings) analyzers by u
|
|||
| `CI_APPLICATION_TAG` | `$CI_COMMIT_SHA` | Docker repository tag for the image to be scanned. | All |
|
||||
| `CS_ANALYZER_IMAGE` | `$SECURE_ANALYZERS_PREFIX/$CS_PROJECT:$CS_MAJOR_VERSION` | Docker image of the analyzer. | All |
|
||||
| `CS_DOCKER_INSECURE` | `"false"` | Allow access to secure Docker registries using HTTPS without validating the certificates. | All |
|
||||
| `CS_REGISTRY_INSECURE` | `"false"` | Allow access to insecure registries (HTTP only). Should only be set to `true` when testing the image locally. | All |
|
||||
| `CS_REGISTRY_INSECURE` | `"false"` | Allow access to insecure registries (HTTP only). Should only be set to `true` when testing the image locally. | Trivy. The registry must listen on port `80/tcp`. |
|
||||
| `CS_SEVERITY_THRESHOLD` | `UNKNOWN` | Severity level threshold. The scanner outputs vulnerabilities with severity level higher than or equal to this threshold. Supported levels are Unknown, Low, Medium, High, and Critical. | Trivy |
|
||||
| `DOCKER_IMAGE` | `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG` | The Docker image to be scanned. If set, this variable overrides the `$CI_APPLICATION_REPOSITORY` and `$CI_APPLICATION_TAG` variables. | All |
|
||||
| `DOCKER_PASSWORD` | `$CI_REGISTRY_PASSWORD` | Password for accessing a Docker registry requiring authentication. | All |
|
||||
|
|
|
@ -4,15 +4,13 @@ module API
|
|||
module Entities
|
||||
class BasicProjectDetails < Entities::ProjectIdentity
|
||||
include ::API::ProjectsRelationBuilder
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
expose :default_branch, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
|
||||
# Avoids an N+1 query: https://github.com/mbleigh/acts-as-taggable-on/issues/91#issuecomment-168273770
|
||||
expose :tag_list do |project|
|
||||
# Tags is a preloaded association. If we perform then sorting
|
||||
# through the database, it will trigger a new query, ending up
|
||||
# in an N+1 if we have several projects
|
||||
project.tags.pluck(:name).sort # rubocop:disable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
||||
expose :topic_names, as: :tag_list
|
||||
expose :topic_names, as: :topics
|
||||
|
||||
expose :ssh_url_to_repo, :http_url_to_repo, :web_url, :readme_url
|
||||
|
||||
|
@ -40,7 +38,7 @@ module API
|
|||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def self.preload_relation(projects_relation, options = {})
|
||||
# Preloading tags, should be done with using only `:tags`,
|
||||
# Preloading topics, should be done with using only `:tags`,
|
||||
# as `:tags` are defined as: `has_many :tags, through: :taggings`
|
||||
# N+1 is solved then by using `subject.tags.map(&:name)`
|
||||
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/20555
|
||||
|
@ -50,6 +48,19 @@ module API
|
|||
.preload(namespace: [:route, :owner])
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
private
|
||||
|
||||
alias_method :project, :object
|
||||
|
||||
def topic_names
|
||||
# Topics is a preloaded association. If we perform then sorting
|
||||
# through the database, it will trigger a new query, ending up
|
||||
# in an N+1 if we have several projects
|
||||
strong_memoize(:topic_names) do
|
||||
project.tags.pluck(:name).sort # rubocop:disable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Caches loading of additional types from the DB
|
||||
# https://github.com/rails/rails/blob/v6.0.3.2/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L521-L589
|
||||
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
module Gitlab
|
||||
module Database
|
||||
module PostgresqlAdapter
|
||||
module TypeMapCache
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
TYPE_MAP_CACHE_MONITOR = ::Monitor.new
|
||||
|
||||
class_methods do
|
||||
def type_map_cache
|
||||
TYPE_MAP_CACHE_MONITOR.synchronize do
|
||||
@type_map_cache ||= {}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_type_map(map = type_map)
|
||||
TYPE_MAP_CACHE_MONITOR.synchronize do
|
||||
cached_type_map = self.class.type_map_cache[@connection_parameters.hash]
|
||||
break @type_map = cached_type_map if cached_type_map
|
||||
|
||||
super
|
||||
self.class.type_map_cache[@connection_parameters.hash] = map
|
||||
end
|
||||
end
|
||||
|
||||
def reload_type_map
|
||||
TYPE_MAP_CACHE_MONITOR.synchronize do
|
||||
self.class.type_map_cache[@connection_parameters.hash] = nil
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -242,7 +242,7 @@ module Gitlab
|
|||
def self.encode(object, limit: 25.megabytes)
|
||||
return ::Gitlab::Json.dump(object) unless Feature.enabled?(:json_limited_encoder)
|
||||
|
||||
buffer = []
|
||||
buffer = StringIO.new
|
||||
buffer_size = 0
|
||||
|
||||
::Yajl::Encoder.encode(object) do |data_chunk|
|
||||
|
@ -254,7 +254,7 @@ module Gitlab
|
|||
buffer_size += chunk_size
|
||||
end
|
||||
|
||||
buffer.join('')
|
||||
buffer.string
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19162,9 +19162,6 @@ msgstr ""
|
|||
msgid "LFS"
|
||||
msgstr ""
|
||||
|
||||
msgid "LFS object"
|
||||
msgstr ""
|
||||
|
||||
msgid "LFS objects"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -23,9 +23,17 @@ module QA
|
|||
#
|
||||
# @return [void]
|
||||
def configure_allure
|
||||
# Match job names like ee:relative, ce:update etc. and set as execution environment
|
||||
env_matcher = /^(?<env>\w{2}:\S+)/
|
||||
|
||||
AllureRspec.configure do |config|
|
||||
config.results_directory = 'tmp/allure-results'
|
||||
config.clean_results_directory = true
|
||||
|
||||
# Set custom environment name to separate same specs executed on different environments
|
||||
if Env.running_in_ci? && Env.ci_job_name.match?(env_matcher)
|
||||
config.environment = Env.ci_job_name.match(env_matcher).named_captures['env']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -67,7 +75,7 @@ module QA
|
|||
issue = example.metadata.dig(:quarantine, :issue)
|
||||
example.issue('Issue', issue) if issue
|
||||
|
||||
example.add_link(name: "Job(#{ENV['CI_JOB_NAME']})", url: ENV['CI_JOB_URL']) if ENV['CI']
|
||||
example.add_link(name: "Job(#{Env.ci_job_name})", url: Env.ci_job_url) if Env.running_in_ci?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -81,7 +81,8 @@ RSpec.describe 'Groups > Members > Manage members' do
|
|||
category: 'Members::CreateService',
|
||||
action: 'create_member',
|
||||
label: 'unknown',
|
||||
property: 'existing_user'
|
||||
property: 'existing_user',
|
||||
user: user1
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -189,7 +190,8 @@ RSpec.describe 'Groups > Members > Manage members' do
|
|||
category: 'Members::InviteService',
|
||||
action: 'create_member',
|
||||
label: 'unknown',
|
||||
property: 'net_new_user'
|
||||
property: 'net_new_user',
|
||||
user: user1
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,14 +30,14 @@ RSpec.describe 'Import/Export - project import integration test', :js do
|
|||
|
||||
it 'user imports an exported project successfully', :sidekiq_might_not_need_inline do
|
||||
visit new_project_path
|
||||
click_import_project_tab
|
||||
click_import_project
|
||||
click_link 'GitLab export'
|
||||
|
||||
fill_in :name, with: 'Test Project Name', visible: true
|
||||
fill_in :path, with: 'test-project-path', visible: true
|
||||
attach_file('file', file)
|
||||
|
||||
expect { click_on 'Import project' }.to change { Project.count }.by(1)
|
||||
expect { click_button 'Import project' }.to change { Project.count }.by(1)
|
||||
|
||||
project = Project.last
|
||||
expect(project).not_to be_nil
|
||||
|
@ -49,11 +49,11 @@ RSpec.describe 'Import/Export - project import integration test', :js do
|
|||
|
||||
visit new_project_path
|
||||
|
||||
click_import_project_tab
|
||||
click_import_project
|
||||
click_link 'GitLab export'
|
||||
fill_in :name, with: project.name, visible: true
|
||||
attach_file('file', file)
|
||||
click_on 'Import project'
|
||||
click_button 'Import project'
|
||||
|
||||
page.within('.flash-container') do
|
||||
expect(page).to have_content('Project could not be imported')
|
||||
|
@ -61,7 +61,7 @@ RSpec.describe 'Import/Export - project import integration test', :js do
|
|||
end
|
||||
end
|
||||
|
||||
def click_import_project_tab
|
||||
def click_import_project
|
||||
find('[data-qa-selector="import_project_link"]').click
|
||||
end
|
||||
end
|
||||
|
|
|
@ -60,7 +60,8 @@ RSpec.describe 'Project members list', :js do
|
|||
category: 'Members::CreateService',
|
||||
action: 'create_member',
|
||||
label: 'unknown',
|
||||
property: 'existing_user'
|
||||
property: 'existing_user',
|
||||
user: user1
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -117,7 +118,8 @@ RSpec.describe 'Project members list', :js do
|
|||
category: 'Members::InviteService',
|
||||
action: 'create_member',
|
||||
label: 'unknown',
|
||||
property: 'net_new_user'
|
||||
property: 'net_new_user',
|
||||
user: user1
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -15,134 +15,151 @@ RSpec.describe 'User views releases', :js do
|
|||
let_it_be(:guest) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(releases_index_apollo_client: false)
|
||||
project.add_maintainer(maintainer)
|
||||
project.add_guest(guest)
|
||||
end
|
||||
|
||||
context('when the user is a maintainer') do
|
||||
before do
|
||||
sign_in(maintainer)
|
||||
shared_examples 'releases index page' do
|
||||
context('when the user is a maintainer') do
|
||||
before do
|
||||
sign_in(maintainer)
|
||||
|
||||
visit project_releases_path(project)
|
||||
end
|
||||
|
||||
it 'sees the release' do
|
||||
page.within("##{release_v1.tag}") do
|
||||
expect(page).to have_content(release_v1.name)
|
||||
expect(page).to have_content(release_v1.tag)
|
||||
expect(page).not_to have_content('Upcoming Release')
|
||||
visit project_releases_path(project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a link as an asset' do
|
||||
let!(:release_link) { create(:release_link, release: release_v1, url: url ) }
|
||||
let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
|
||||
let(:direct_asset_link) { Gitlab::Routing.url_helpers.project_release_url(project, release_v1) << "/downloads#{release_link.filepath}" }
|
||||
|
||||
it 'sees the link' do
|
||||
page.within("##{release_v1.tag} .js-assets-list") do
|
||||
expect(page).to have_link release_link.name, href: direct_asset_link
|
||||
expect(page).not_to have_css('[data-testid="external-link-indicator"]')
|
||||
it 'sees the release' do
|
||||
page.within("##{release_v1.tag}") do
|
||||
expect(page).to have_content(release_v1.name)
|
||||
expect(page).to have_content(release_v1.tag)
|
||||
expect(page).not_to have_content('Upcoming Release')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a link redirect' do
|
||||
let!(:release_link) { create(:release_link, release: release_v1, name: 'linux-amd64 binaries', filepath: '/binaries/linux-amd64', url: url) }
|
||||
context 'when there is a link as an asset' do
|
||||
let!(:release_link) { create(:release_link, release: release_v1, url: url ) }
|
||||
let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
|
||||
let(:direct_asset_link) { Gitlab::Routing.url_helpers.project_release_url(project, release_v1) << "/downloads#{release_link.filepath}" }
|
||||
|
||||
it 'sees the link', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/329301' do
|
||||
it 'sees the link' do
|
||||
page.within("##{release_v1.tag} .js-assets-list") do
|
||||
expect(page).to have_link release_link.name, href: direct_asset_link
|
||||
expect(page).not_to have_css('[data-testid="external-link-indicator"]')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when url points to external resource' do
|
||||
let(:url) { 'http://google.com/download' }
|
||||
context 'when there is a link redirect' do
|
||||
let!(:release_link) { create(:release_link, release: release_v1, name: 'linux-amd64 binaries', filepath: '/binaries/linux-amd64', url: url) }
|
||||
let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
|
||||
|
||||
it 'sees that the link is external resource', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/329302' do
|
||||
page.within("##{release_v1.tag} .js-assets-list") do
|
||||
expect(page).to have_css('[data-testid="external-link-indicator"]')
|
||||
it 'sees the link', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/329301' do
|
||||
page.within("##{release_v1.tag} .js-assets-list") do
|
||||
expect(page).to have_link release_link.name, href: direct_asset_link
|
||||
expect(page).not_to have_css('[data-testid="external-link-indicator"]')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an upcoming release' do
|
||||
it 'sees the upcoming tag' do
|
||||
page.within("##{release_v3.tag}") do
|
||||
expect(page).to have_content('Upcoming Release')
|
||||
end
|
||||
end
|
||||
end
|
||||
context 'when url points to external resource' do
|
||||
let(:url) { 'http://google.com/download' }
|
||||
|
||||
context 'with a tag containing a slash' do
|
||||
it 'sees the release' do
|
||||
page.within("##{release_v2.tag.parameterize}") do
|
||||
expect(page).to have_content(release_v2.name)
|
||||
expect(page).to have_content(release_v2.tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'sorting' do
|
||||
def sort_page(by:, direction:)
|
||||
within '[data-testid="releases-sort"]' do
|
||||
find('.dropdown-toggle').click
|
||||
|
||||
click_button(by, class: 'dropdown-item')
|
||||
|
||||
find('.sorting-direction-button').click if direction == :ascending
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'releases sort order' do
|
||||
it "sorts the releases #{description}" do
|
||||
card_titles = page.all('.release-block .card-title', minimum: expected_releases.count)
|
||||
|
||||
card_titles.each_with_index do |title, index|
|
||||
expect(title).to have_content(expected_releases[index].name)
|
||||
it 'sees that the link is external resource', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/329302' do
|
||||
page.within("##{release_v1.tag} .js-assets-list") do
|
||||
expect(page).to have_css('[data-testid="external-link-indicator"]')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the page is sorted by the default sort order" do
|
||||
let(:expected_releases) { [release_v3, release_v2, release_v1] }
|
||||
|
||||
it_behaves_like 'releases sort order'
|
||||
context 'with an upcoming release' do
|
||||
it 'sees the upcoming tag' do
|
||||
page.within("##{release_v3.tag}") do
|
||||
expect(page).to have_content('Upcoming Release')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the page is sorted by created_at ascending " do
|
||||
let(:expected_releases) { [release_v2, release_v1, release_v3] }
|
||||
context 'with a tag containing a slash' do
|
||||
it 'sees the release' do
|
||||
page.within("##{release_v2.tag.parameterize}") do
|
||||
expect(page).to have_content(release_v2.name)
|
||||
expect(page).to have_content(release_v2.tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
sort_page by: 'Created date', direction: :ascending
|
||||
context 'sorting' do
|
||||
def sort_page(by:, direction:)
|
||||
within '[data-testid="releases-sort"]' do
|
||||
find('.dropdown-toggle').click
|
||||
|
||||
click_button(by, class: 'dropdown-item')
|
||||
|
||||
find('.sorting-direction-button').click if direction == :ascending
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'releases sort order'
|
||||
shared_examples 'releases sort order' do
|
||||
it "sorts the releases #{description}" do
|
||||
card_titles = page.all('.release-block .card-title', minimum: expected_releases.count)
|
||||
|
||||
card_titles.each_with_index do |title, index|
|
||||
expect(title).to have_content(expected_releases[index].name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the page is sorted by the default sort order" do
|
||||
let(:expected_releases) { [release_v3, release_v2, release_v1] }
|
||||
|
||||
it_behaves_like 'releases sort order'
|
||||
end
|
||||
|
||||
context "when the page is sorted by created_at ascending " do
|
||||
let(:expected_releases) { [release_v2, release_v1, release_v3] }
|
||||
|
||||
before do
|
||||
sort_page by: 'Created date', direction: :ascending
|
||||
end
|
||||
|
||||
it_behaves_like 'releases sort order'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context('when the user is a guest') do
|
||||
before do
|
||||
sign_in(guest)
|
||||
end
|
||||
|
||||
it 'renders release info except for Git-related data' do
|
||||
visit project_releases_path(project)
|
||||
|
||||
within('.release-block', match: :first) do
|
||||
expect(page).to have_content(release_v3.description)
|
||||
|
||||
# The following properties (sometimes) include Git info,
|
||||
# so they are not rendered for Guest users
|
||||
expect(page).not_to have_content(release_v3.name)
|
||||
expect(page).not_to have_content(release_v3.tag)
|
||||
expect(page).not_to have_content(release_v3.commit.short_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context('when the user is a guest') do
|
||||
context 'when the releases_index_apollo_client feature flag is enabled' do
|
||||
before do
|
||||
sign_in(guest)
|
||||
stub_feature_flags(releases_index_apollo_client: true)
|
||||
end
|
||||
|
||||
it 'renders release info except for Git-related data' do
|
||||
visit project_releases_path(project)
|
||||
it_behaves_like 'releases index page'
|
||||
end
|
||||
|
||||
within('.release-block', match: :first) do
|
||||
expect(page).to have_content(release_v3.description)
|
||||
|
||||
# The following properties (sometimes) include Git info,
|
||||
# so they are not rendered for Guest users
|
||||
expect(page).not_to have_content(release_v3.name)
|
||||
expect(page).not_to have_content(release_v3.tag)
|
||||
expect(page).not_to have_content(release_v3.commit.short_id)
|
||||
end
|
||||
context 'when the releases_index_apollo_client feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(releases_index_apollo_client: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'releases index page'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"description",
|
||||
"default_branch",
|
||||
"tag_list",
|
||||
"topics",
|
||||
"ssh_url_to_repo",
|
||||
"http_url_to_repo",
|
||||
"web_url",
|
||||
|
@ -34,6 +35,7 @@
|
|||
"description": { "type": ["string", "null"] },
|
||||
"default_branch": { "type": ["string", "null"] },
|
||||
"tag_list": { "type": "array" },
|
||||
"topics": { "type": "array" },
|
||||
"ssh_url_to_repo": { "type": "string" },
|
||||
"http_url_to_repo": { "type": "string" },
|
||||
"web_url": { "type": "string" },
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"topics": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"ssh_url_to_repo": { "type": "string" },
|
||||
"http_url_to_repo": { "type": "string" },
|
||||
"web_url": { "type": "string" },
|
||||
|
@ -37,7 +43,7 @@
|
|||
},
|
||||
"required": [
|
||||
"id", "name", "name_with_namespace", "description", "path",
|
||||
"path_with_namespace", "created_at", "default_branch", "tag_list",
|
||||
"path_with_namespace", "created_at", "default_branch", "tag_list", "topics",
|
||||
"ssh_url_to_repo", "http_url_to_repo", "web_url", "readme_url", "avatar_url",
|
||||
"star_count", "forks_count", "last_activity_at", "namespace"
|
||||
],
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"description": "",
|
||||
"default_branch": null,
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"public": true,
|
||||
"archived": false,
|
||||
"visibility_level": 20,
|
||||
|
@ -54,6 +55,7 @@
|
|||
"description": "Voluptatem quae nulla eius numquam ullam voluptatibus quia modi.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"public": false,
|
||||
"archived": false,
|
||||
"visibility_level": 0,
|
||||
|
@ -114,6 +116,7 @@
|
|||
"description": "Modi odio mollitia dolorem qui.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"public": false,
|
||||
"archived": false,
|
||||
"visibility_level": 0,
|
||||
|
@ -162,6 +165,7 @@
|
|||
"description": "Omnis asperiores ipsa et beatae quidem necessitatibus quia.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"public": true,
|
||||
"archived": false,
|
||||
"visibility_level": 20,
|
||||
|
@ -210,6 +214,7 @@
|
|||
"description": "Voluptatem commodi voluptate placeat architecto beatae illum dolores fugiat.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"public": false,
|
||||
"archived": false,
|
||||
"visibility_level": 0,
|
||||
|
@ -258,6 +263,7 @@
|
|||
"description": "Aut molestias quas est ut aperiam officia quod libero.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"public": true,
|
||||
"archived": false,
|
||||
"visibility_level": 20,
|
||||
|
@ -309,6 +315,7 @@
|
|||
"description": "Excepturi molestiae quia repellendus omnis est illo illum eligendi.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"public": true,
|
||||
"archived": false,
|
||||
"visibility_level": 20,
|
||||
|
@ -357,6 +364,7 @@
|
|||
"description": "Adipisci quaerat dignissimos enim sed ipsam dolorem quia.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"public": false,
|
||||
"archived": false,
|
||||
"visibility_level": 10,
|
||||
|
@ -408,6 +416,7 @@
|
|||
"description": "Vel voluptatem maxime saepe ex quia.",
|
||||
"default_branch": "master",
|
||||
"tag_list": [],
|
||||
"topics": [],
|
||||
"public": false,
|
||||
"archived": false,
|
||||
"visibility_level": 0,
|
||||
|
|
|
@ -7,8 +7,8 @@ import EditDrawer from '~/static_site_editor/components/edit_drawer.vue';
|
|||
import EditHeader from '~/static_site_editor/components/edit_header.vue';
|
||||
import PublishToolbar from '~/static_site_editor/components/publish_toolbar.vue';
|
||||
import UnsavedChangesConfirmDialog from '~/static_site_editor/components/unsaved_changes_confirm_dialog.vue';
|
||||
import { EDITOR_TYPES } from '~/vue_shared/components/rich_content_editor/constants';
|
||||
import RichContentEditor from '~/vue_shared/components/rich_content_editor/rich_content_editor.vue';
|
||||
import { EDITOR_TYPES } from '~/static_site_editor/rich_content_editor/constants';
|
||||
import RichContentEditor from '~/static_site_editor/rich_content_editor/rich_content_editor.vue';
|
||||
|
||||
import {
|
||||
sourceContentTitle as title,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import buildCustomRenderer from '~/vue_shared/components/rich_content_editor/services/build_custom_renderer';
|
||||
import buildHTMLToMarkdownRenderer from '~/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer';
|
||||
import buildCustomRenderer from '~/static_site_editor/rich_content_editor/services/build_custom_renderer';
|
||||
import buildHTMLToMarkdownRenderer from '~/static_site_editor/rich_content_editor/services/build_html_to_markdown_renderer';
|
||||
import {
|
||||
generateToolbarItem,
|
||||
addCustomEventListener,
|
||||
|
@ -9,12 +9,12 @@ import {
|
|||
insertVideo,
|
||||
getMarkdown,
|
||||
getEditorOptions,
|
||||
} from '~/vue_shared/components/rich_content_editor/services/editor_service';
|
||||
import sanitizeHTML from '~/vue_shared/components/rich_content_editor/services/sanitize_html';
|
||||
} from '~/static_site_editor/rich_content_editor/services/editor_service';
|
||||
import sanitizeHTML from '~/static_site_editor/rich_content_editor/services/sanitize_html';
|
||||
|
||||
jest.mock('~/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer');
|
||||
jest.mock('~/vue_shared/components/rich_content_editor/services/build_custom_renderer');
|
||||
jest.mock('~/vue_shared/components/rich_content_editor/services/sanitize_html');
|
||||
jest.mock('~/static_site_editor/rich_content_editor/services/build_html_to_markdown_renderer');
|
||||
jest.mock('~/static_site_editor/rich_content_editor/services/build_custom_renderer');
|
||||
jest.mock('~/static_site_editor/rich_content_editor/services/sanitize_html');
|
||||
|
||||
describe('Editor Service', () => {
|
||||
let mockInstance;
|
|
@ -1,8 +1,8 @@
|
|||
import { GlModal, GlTabs } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { IMAGE_TABS } from '~/vue_shared/components/rich_content_editor/constants';
|
||||
import AddImageModal from '~/vue_shared/components/rich_content_editor/modals/add_image/add_image_modal.vue';
|
||||
import UploadImageTab from '~/vue_shared/components/rich_content_editor/modals/add_image/upload_image_tab.vue';
|
||||
import { IMAGE_TABS } from '~/static_site_editor/rich_content_editor/constants';
|
||||
import AddImageModal from '~/static_site_editor/rich_content_editor/modals/add_image/add_image_modal.vue';
|
||||
import UploadImageTab from '~/static_site_editor/rich_content_editor/modals/add_image/upload_image_tab.vue';
|
||||
|
||||
describe('Add Image Modal', () => {
|
||||
let wrapper;
|
|
@ -1,5 +1,5 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import UploadImageTab from '~/vue_shared/components/rich_content_editor/modals/add_image/upload_image_tab.vue';
|
||||
import UploadImageTab from '~/static_site_editor/rich_content_editor/modals/add_image/upload_image_tab.vue';
|
||||
|
||||
describe('Upload Image Tab', () => {
|
||||
let wrapper;
|
|
@ -1,6 +1,6 @@
|
|||
import { GlModal } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import InsertVideoModal from '~/vue_shared/components/rich_content_editor/modals/insert_video_modal.vue';
|
||||
import InsertVideoModal from '~/static_site_editor/rich_content_editor/modals/insert_video_modal.vue';
|
||||
|
||||
describe('Insert Video Modal', () => {
|
||||
let wrapper;
|
|
@ -1,8 +1,8 @@
|
|||
import Editor from '@toast-ui/editor';
|
||||
import buildMarkdownToHTMLRenderer from '~/vue_shared/components/rich_content_editor/services/build_custom_renderer';
|
||||
import { registerHTMLToMarkdownRenderer } from '~/vue_shared/components/rich_content_editor/services/editor_service';
|
||||
import buildMarkdownToHTMLRenderer from '~/static_site_editor/rich_content_editor/services/build_custom_renderer';
|
||||
import { registerHTMLToMarkdownRenderer } from '~/static_site_editor/rich_content_editor/services/editor_service';
|
||||
|
||||
describe('vue_shared/components/rich_content_editor', () => {
|
||||
describe('static_site_editor/rich_content_editor', () => {
|
||||
let editor;
|
||||
|
||||
const buildEditor = () => {
|
|
@ -5,10 +5,10 @@ import {
|
|||
EDITOR_HEIGHT,
|
||||
EDITOR_PREVIEW_STYLE,
|
||||
CUSTOM_EVENTS,
|
||||
} from '~/vue_shared/components/rich_content_editor/constants';
|
||||
import AddImageModal from '~/vue_shared/components/rich_content_editor/modals/add_image/add_image_modal.vue';
|
||||
import InsertVideoModal from '~/vue_shared/components/rich_content_editor/modals/insert_video_modal.vue';
|
||||
import RichContentEditor from '~/vue_shared/components/rich_content_editor/rich_content_editor.vue';
|
||||
} from '~/static_site_editor/rich_content_editor/constants';
|
||||
import AddImageModal from '~/static_site_editor/rich_content_editor/modals/add_image/add_image_modal.vue';
|
||||
import InsertVideoModal from '~/static_site_editor/rich_content_editor/modals/insert_video_modal.vue';
|
||||
import RichContentEditor from '~/static_site_editor/rich_content_editor/rich_content_editor.vue';
|
||||
|
||||
import {
|
||||
addCustomEventListener,
|
||||
|
@ -18,9 +18,9 @@ import {
|
|||
registerHTMLToMarkdownRenderer,
|
||||
getEditorOptions,
|
||||
getMarkdown,
|
||||
} from '~/vue_shared/components/rich_content_editor/services/editor_service';
|
||||
} from '~/static_site_editor/rich_content_editor/services/editor_service';
|
||||
|
||||
jest.mock('~/vue_shared/components/rich_content_editor/services/editor_service', () => ({
|
||||
jest.mock('~/static_site_editor/rich_content_editor/services/editor_service', () => ({
|
||||
addCustomEventListener: jest.fn(),
|
||||
removeCustomEventListener: jest.fn(),
|
||||
addImage: jest.fn(),
|
|
@ -1,4 +1,4 @@
|
|||
import buildCustomHTMLRenderer from '~/vue_shared/components/rich_content_editor/services/build_custom_renderer';
|
||||
import buildCustomHTMLRenderer from '~/static_site_editor/rich_content_editor/services/build_custom_renderer';
|
||||
|
||||
describe('Build Custom Renderer Service', () => {
|
||||
describe('buildCustomHTMLRenderer', () => {
|
|
@ -1,4 +1,4 @@
|
|||
import buildHTMLToMarkdownRenderer from '~/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer';
|
||||
import buildHTMLToMarkdownRenderer from '~/static_site_editor/rich_content_editor/services/build_html_to_markdown_renderer';
|
||||
import { attributeDefinition } from './renderers/mock_data';
|
||||
|
||||
describe('rich_content_editor/services/html_to_markdown_renderer', () => {
|
|
@ -6,7 +6,7 @@ import {
|
|||
buildUneditableBlockTokens,
|
||||
buildUneditableInlineTokens,
|
||||
buildUneditableHtmlAsTextTokens,
|
||||
} from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
} from '~/static_site_editor/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
|
||||
import {
|
||||
originInlineToken,
|
|
@ -1,4 +1,4 @@
|
|||
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_attribute_definition';
|
||||
import renderer from '~/static_site_editor/rich_content_editor/services/renderers/render_attribute_definition';
|
||||
import { attributeDefinition } from './mock_data';
|
||||
|
||||
describe('rich_content_editor/renderers/render_attribute_definition', () => {
|
|
@ -1,5 +1,5 @@
|
|||
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_embedded_ruby_text';
|
||||
import { renderUneditableLeaf } from '~/vue_shared/components/rich_content_editor/services/renderers/render_utils';
|
||||
import renderer from '~/static_site_editor/rich_content_editor/services/renderers/render_embedded_ruby_text';
|
||||
import { renderUneditableLeaf } from '~/static_site_editor/rich_content_editor/services/renderers/render_utils';
|
||||
|
||||
import { buildMockTextNode, normalTextNode } from './mock_data';
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { buildUneditableInlineTokens } from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_font_awesome_html_inline';
|
||||
import { buildUneditableInlineTokens } from '~/static_site_editor/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
import renderer from '~/static_site_editor/rich_content_editor/services/renderers/render_font_awesome_html_inline';
|
||||
|
||||
import { normalTextNode } from './mock_data';
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_heading';
|
||||
import * as renderUtils from '~/vue_shared/components/rich_content_editor/services/renderers/render_utils';
|
||||
import renderer from '~/static_site_editor/rich_content_editor/services/renderers/render_heading';
|
||||
import * as renderUtils from '~/static_site_editor/rich_content_editor/services/renderers/render_utils';
|
||||
|
||||
describe('rich_content_editor/renderers/render_heading', () => {
|
||||
it('canRender delegates to renderUtils.willAlwaysRender', () => {
|
|
@ -1,5 +1,5 @@
|
|||
import { buildUneditableHtmlAsTextTokens } from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_html_block';
|
||||
import { buildUneditableHtmlAsTextTokens } from '~/static_site_editor/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
import renderer from '~/static_site_editor/rich_content_editor/services/renderers/render_html_block';
|
||||
|
||||
describe('rich_content_editor/services/renderers/render_html_block', () => {
|
||||
const htmlBlockNode = {
|
|
@ -1,5 +1,5 @@
|
|||
import { buildUneditableInlineTokens } from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_identifier_instance_text';
|
||||
import { buildUneditableInlineTokens } from '~/static_site_editor/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
import renderer from '~/static_site_editor/rich_content_editor/services/renderers/render_identifier_instance_text';
|
||||
|
||||
import { buildMockTextNode, normalTextNode } from './mock_data';
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_identifier_paragraph';
|
||||
import renderer from '~/static_site_editor/rich_content_editor/services/renderers/render_identifier_paragraph';
|
||||
|
||||
import { buildMockTextNode } from './mock_data';
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_list_item';
|
||||
import * as renderUtils from '~/vue_shared/components/rich_content_editor/services/renderers/render_utils';
|
||||
import renderer from '~/static_site_editor/rich_content_editor/services/renderers/render_list_item';
|
||||
import * as renderUtils from '~/static_site_editor/rich_content_editor/services/renderers/render_utils';
|
||||
|
||||
describe('rich_content_editor/renderers/render_list_item', () => {
|
||||
it('canRender delegates to renderUtils.willAlwaysRender', () => {
|
|
@ -1,4 +1,4 @@
|
|||
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_softbreak';
|
||||
import renderer from '~/static_site_editor/rich_content_editor/services/renderers/render_softbreak';
|
||||
|
||||
describe('Render softbreak renderer', () => {
|
||||
describe('canRender', () => {
|
|
@ -1,13 +1,13 @@
|
|||
import {
|
||||
buildUneditableBlockTokens,
|
||||
buildUneditableOpenTokens,
|
||||
} from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
} from '~/static_site_editor/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
import {
|
||||
renderUneditableLeaf,
|
||||
renderUneditableBranch,
|
||||
renderWithAttributeDefinitions,
|
||||
willAlwaysRender,
|
||||
} from '~/vue_shared/components/rich_content_editor/services/renderers/render_utils';
|
||||
} from '~/static_site_editor/rich_content_editor/services/renderers/render_utils';
|
||||
|
||||
import { originToken, uneditableCloseToken, attributeDefinition } from './mock_data';
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import sanitizeHTML from '~/vue_shared/components/rich_content_editor/services/sanitize_html';
|
||||
import sanitizeHTML from '~/static_site_editor/rich_content_editor/services/sanitize_html';
|
||||
|
||||
describe('rich_content_editor/services/sanitize_html', () => {
|
||||
it.each`
|
|
@ -1,7 +1,7 @@
|
|||
import { GlIcon } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import ToolbarItem from '~/vue_shared/components/rich_content_editor/toolbar_item.vue';
|
||||
import ToolbarItem from '~/static_site_editor/rich_content_editor/toolbar_item.vue';
|
||||
|
||||
describe('Toolbar Item', () => {
|
||||
let wrapper;
|
|
@ -0,0 +1,75 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Database::PostgresqlAdapter::TypeMapCache do
|
||||
let(:db_config) { ActiveRecord::Base.configurations.configs_for(env_name: 'test', name: 'primary').configuration_hash }
|
||||
let(:adapter_class) do
|
||||
Class.new(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
||||
end
|
||||
|
||||
describe '#initialize_type_map' do
|
||||
it 'caches loading of types in memory' do
|
||||
initialize_connection.disconnect!
|
||||
recorder_without_cache = ActiveRecord::QueryRecorder.new(skip_schema_queries: false) { initialize_connection.disconnect! }
|
||||
|
||||
expect(recorder_without_cache.log).to include(a_string_matching(/FROM pg_type/)).twice
|
||||
|
||||
adapter_class.prepend(described_class)
|
||||
|
||||
initialize_connection.disconnect!
|
||||
recorder_with_cache = ActiveRecord::QueryRecorder.new(skip_schema_queries: false) { initialize_connection.disconnect! }
|
||||
|
||||
expect(recorder_with_cache.count).to be < recorder_without_cache.count
|
||||
|
||||
# There's still one pg_type query left here because `#add_pg_decoders` executes another pg_type query
|
||||
# in https://github.com/rails/rails/blob/v6.1.3.2/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L912.
|
||||
# This query is much cheaper because it only returns very few records.
|
||||
expect(recorder_with_cache.log).to include(a_string_matching(/FROM pg_type/)).once
|
||||
end
|
||||
|
||||
it 'only reuses the cache if the connection parameters are exactly the same' do
|
||||
adapter_class.prepend(described_class)
|
||||
|
||||
initialize_connection.disconnect!
|
||||
|
||||
other_config = db_config.dup
|
||||
other_config[:connect_timeout] = db_config[:connect_timeout].to_i + 10
|
||||
|
||||
recorder = ActiveRecord::QueryRecorder.new(skip_schema_queries: false) { initialize_connection(other_config).disconnect! }
|
||||
|
||||
expect(recorder.log).to include(a_string_matching(/FROM pg_type/)).twice
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reload_type_map' do
|
||||
it 'clears the cache and executes the type map query again' do
|
||||
adapter_class.prepend(described_class)
|
||||
|
||||
initialize_connection.disconnect!
|
||||
|
||||
connection = initialize_connection
|
||||
recorder = ActiveRecord::QueryRecorder.new(skip_schema_queries: false) { connection.reload_type_map }
|
||||
|
||||
expect(recorder.log).to include(a_string_matching(/FROM pg_type/)).once
|
||||
end
|
||||
end
|
||||
|
||||
# Based on https://github.com/rails/rails/blob/v6.1.3.2/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L36-L41
|
||||
def initialize_connection(config = db_config)
|
||||
conn_params = config.symbolize_keys.compact
|
||||
|
||||
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
||||
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
||||
|
||||
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
||||
conn_params.slice!(*valid_conn_param_keys)
|
||||
|
||||
adapter_class.new(
|
||||
adapter_class.new_client(conn_params),
|
||||
ActiveRecord::Base.logger,
|
||||
conn_params,
|
||||
config
|
||||
)
|
||||
end
|
||||
end
|
|
@ -411,7 +411,7 @@ RSpec.describe Gitlab::Json do
|
|||
end
|
||||
|
||||
describe Gitlab::Json::LimitedEncoder do
|
||||
subject { described_class.encode(obj, limit: 8.kilobytes) }
|
||||
subject { described_class.encode(obj, limit: 10.kilobytes) }
|
||||
|
||||
context 'when object size is acceptable' do
|
||||
let(:obj) { { test: true } }
|
||||
|
@ -431,6 +431,16 @@ RSpec.describe Gitlab::Json do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when object contains ASCII-8BIT encoding' do
|
||||
let(:obj) { [{ a: "\x8F" }] * 1000 }
|
||||
|
||||
it 'does not raise encoding error' do
|
||||
expect { subject }.not_to raise_error
|
||||
expect(subject).to be_a(String)
|
||||
expect(subject.size).to eq(10001)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when json_limited_encoder is disabled' do
|
||||
let(:obj) { [{ test: true }] * 1000 }
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ itself: # project
|
|||
- shared_with_groups
|
||||
- ssh_url_to_repo
|
||||
- tag_list
|
||||
- topics
|
||||
- web_url
|
||||
|
||||
build_auto_devops: # auto_devops
|
||||
|
|
|
@ -184,13 +184,14 @@ RSpec.describe API::Projects do
|
|||
end
|
||||
end
|
||||
|
||||
it 'includes the project labels as the tag_list' do
|
||||
it 'includes project topics' do
|
||||
get api('/projects', user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.first.keys).to include('tag_list')
|
||||
expect(json_response.first.keys).to include('tag_list') # deprecated in favor of 'topics'
|
||||
expect(json_response.first.keys).to include('topics')
|
||||
end
|
||||
|
||||
it 'includes open_issues_count' do
|
||||
|
@ -1892,7 +1893,8 @@ RSpec.describe API::Projects do
|
|||
expect(json_response['id']).to eq(project.id)
|
||||
expect(json_response['description']).to eq(project.description)
|
||||
expect(json_response['default_branch']).to eq(project.default_branch)
|
||||
expect(json_response['tag_list']).to be_an Array
|
||||
expect(json_response['tag_list']).to be_an Array # deprecated in favor of 'topics'
|
||||
expect(json_response['topics']).to be_an Array
|
||||
expect(json_response['archived']).to be_falsey
|
||||
expect(json_response['visibility']).to be_present
|
||||
expect(json_response['ssh_url_to_repo']).to be_present
|
||||
|
@ -1969,7 +1971,8 @@ RSpec.describe API::Projects do
|
|||
expect(json_response['id']).to eq(project.id)
|
||||
expect(json_response['description']).to eq(project.description)
|
||||
expect(json_response['default_branch']).to eq(project.default_branch)
|
||||
expect(json_response['tag_list']).to be_an Array
|
||||
expect(json_response['tag_list']).to be_an Array # deprecated in favor of 'topics'
|
||||
expect(json_response['topics']).to be_an Array
|
||||
expect(json_response['archived']).to be_falsey
|
||||
expect(json_response['visibility']).to be_present
|
||||
expect(json_response['ssh_url_to_repo']).to be_present
|
||||
|
|
|
@ -2,15 +2,16 @@
|
|||
|
||||
module ActiveRecord
|
||||
class QueryRecorder
|
||||
attr_reader :log, :skip_cached, :cached, :data
|
||||
attr_reader :log, :skip_cached, :skip_schema_queries, :cached, :data
|
||||
|
||||
UNKNOWN = %w[unknown unknown].freeze
|
||||
|
||||
def initialize(skip_cached: true, log_file: nil, query_recorder_debug: false, &block)
|
||||
def initialize(skip_cached: true, skip_schema_queries: true, log_file: nil, query_recorder_debug: false, &block)
|
||||
@data = Hash.new { |h, k| h[k] = { count: 0, occurrences: [], backtrace: [], durations: [] } }
|
||||
@log = []
|
||||
@cached = []
|
||||
@skip_cached = skip_cached
|
||||
@skip_schema_queries = skip_schema_queries
|
||||
@query_recorder_debug = ENV['QUERY_RECORDER_DEBUG'] || query_recorder_debug
|
||||
@log_file = log_file
|
||||
record(&block) if block_given?
|
||||
|
@ -79,7 +80,7 @@ module ActiveRecord
|
|||
|
||||
if values[:cached] && skip_cached
|
||||
@cached << values[:sql]
|
||||
elsif !values[:name]&.include?("SCHEMA")
|
||||
elsif !skip_schema_queries || !values[:name]&.include?("SCHEMA")
|
||||
backtrace = @query_recorder_debug ? show_backtrace(values, duration) : nil
|
||||
@log << values[:sql]
|
||||
store_sql_by_source(values: values, duration: duration, backtrace: backtrace)
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -52,6 +53,14 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string)
|
|||
}
|
||||
}
|
||||
|
||||
func detectFileContentType(fileName string) string {
|
||||
contentType := mime.TypeByExtension(filepath.Ext(fileName))
|
||||
if contentType == "" {
|
||||
contentType = "application/octet-stream"
|
||||
}
|
||||
return contentType
|
||||
}
|
||||
|
||||
func unpackFileFromZip(ctx context.Context, archivePath, encodedFilename string, headers http.Header, output io.Writer) error {
|
||||
fileName, err := zipartifacts.DecodeFileEntry(encodedFilename)
|
||||
if err != nil {
|
||||
|
@ -88,15 +97,7 @@ func unpackFileFromZip(ctx context.Context, archivePath, encodedFilename string,
|
|||
|
||||
// Write http headers about the file
|
||||
headers.Set("Content-Length", contentLength)
|
||||
|
||||
// Using application/octet-stream tells the client that we don't
|
||||
// really know what Content-Type is. Since this file is being sent
|
||||
// as attachment, browsers don't need to know to save the
|
||||
// file. Chrome doesn't appear to pay attention to Content-Type when
|
||||
// Content-Disposition is an attachment, and Firefox only uses it if there
|
||||
// is no extension in the filename. Thus, there's no need for
|
||||
// Workhorse to guess Content-Type based on the filename.
|
||||
headers.Set("Content-Type", "application/octet-stream")
|
||||
headers.Set("Content-Type", detectFileContentType(fileName))
|
||||
headers.Set("Content-Disposition", "attachment; filename=\""+escapeQuotes(basename)+"\"")
|
||||
// Copy file body to client
|
||||
if _, err := io.Copy(output, reader); err != nil {
|
||||
|
|
|
@ -54,7 +54,7 @@ func TestDownloadingFromValidArchive(t *testing.T) {
|
|||
|
||||
testhelper.RequireResponseHeader(t, response,
|
||||
"Content-Type",
|
||||
"application/octet-stream")
|
||||
"text/plain; charset=utf-8")
|
||||
testhelper.RequireResponseHeader(t, response,
|
||||
"Content-Disposition",
|
||||
"attachment; filename=\"test.txt\"")
|
||||
|
@ -88,7 +88,7 @@ func TestDownloadingFromValidHTTPArchive(t *testing.T) {
|
|||
|
||||
testhelper.RequireResponseHeader(t, response,
|
||||
"Content-Type",
|
||||
"application/octet-stream")
|
||||
"text/plain; charset=utf-8")
|
||||
testhelper.RequireResponseHeader(t, response,
|
||||
"Content-Disposition",
|
||||
"attachment; filename=\"test.txt\"")
|
||||
|
|
Loading…
Reference in New Issue