Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-02-10 00:09:33 +00:00
parent b5944525b0
commit 5a120c32fd
44 changed files with 564 additions and 145 deletions

View File

@ -12,6 +12,10 @@ export default {
required: true,
type: String,
},
selector: {
required: true,
type: String,
},
},
data() {
return {
@ -34,22 +38,24 @@ export default {
},
mounted() {
document.addEventListener('click', this.handleClick);
},
/*
* Here we're looking for every button that needs to launch a modal
* on click, and then attaching a click event handler to show the modal
* if it's correctly configured.
*
* TODO: Replace this with integrated modal components https://gitlab.com/gitlab-org/gitlab/-/issues/320922
*/
document.querySelectorAll(this.selector).forEach((button) => {
button.addEventListener('click', (e) => {
if (!button.dataset.glModalAction) return;
beforeDestroy() {
document.removeEventListener('click', this.handleClick);
e.preventDefault();
this.show(button.dataset);
});
});
},
methods: {
handleClick(e) {
const { glModalAction: action } = e.target.dataset;
if (!action) return;
this.show(e.target.dataset);
e.preventDefault();
},
show(modalData) {
const { glModalAction: requestedAction } = modalData;

View File

@ -7,6 +7,7 @@ import { initAdminUsersApp, initCohortsEmptyState } from '~/admin/users';
import initTabs from '~/admin/users/tabs';
import ModalManager from './components/user_modal_manager.vue';
const CONFIRM_DELETE_BUTTON_SELECTOR = '.js-delete-user-modal-button';
const MODAL_TEXTS_CONTAINER_SELECTOR = '#js-modal-texts';
const MODAL_MANAGER_SELECTOR = '#js-delete-user-modal';
@ -50,6 +51,7 @@ document.addEventListener('DOMContentLoaded', () => {
return h(ModalManager, {
ref: 'manager',
props: {
selector: CONFIRM_DELETE_BUTTON_SELECTOR,
modalConfiguration,
csrfToken: csrf.token,
},

View File

@ -525,7 +525,7 @@
&.is-active {
/* stylelint-disable-next-line function-url-quotes */
background: url(asset_path('checkmark.png')) no-repeat 14px 8px;
background: url(asset_path('checkmark.png')) no-repeat 14px center;
}
}
}

View File

@ -5,7 +5,7 @@ module Packages
class PackageFinder
include ::Packages::FinderHelper
MAX_PACKAGES_COUNT = 50
MAX_PACKAGES_COUNT = 300
def initialize(current_user, project_or_group, package_name:, package_version: nil, limit: MAX_PACKAGES_COUNT)
@current_user = current_user

View File

@ -17,6 +17,7 @@ class Packages::Package < ApplicationRecord
has_one :maven_metadatum, inverse_of: :package, class_name: 'Packages::Maven::Metadatum'
has_one :nuget_metadatum, inverse_of: :package, class_name: 'Packages::Nuget::Metadatum'
has_one :composer_metadatum, inverse_of: :package, class_name: 'Packages::Composer::Metadatum'
has_one :rubygems_metadatum, inverse_of: :package, class_name: 'Packages::Rubygems::Metadatum'
has_many :build_infos, inverse_of: :package
has_many :pipelines, through: :build_infos
has_one :debian_publication, inverse_of: :package, class_name: 'Packages::Debian::Publication'
@ -64,7 +65,9 @@ class Packages::Package < ApplicationRecord
if: :debian_package?
validate :forbidden_debian_changes, if: :debian?
enum package_type: { maven: 1, npm: 2, conan: 3, nuget: 4, pypi: 5, composer: 6, generic: 7, golang: 8, debian: 9 }
enum package_type: { maven: 1, npm: 2, conan: 3, nuget: 4, pypi: 5,
composer: 6, generic: 7, golang: 8, debian: 9,
rubygems: 10 }
scope :with_name, ->(name) { where(name: name) }
scope :with_name_like, ->(name) { where(arel_table[:name].matches(name)) }

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
module Packages
module Rubygems
class Metadatum < ApplicationRecord
self.table_name = 'packages_rubygems_metadata'
self.primary_key = :package_id
belongs_to :package, -> { where(package_type: :rubygems) }, inverse_of: :rubygems_metadatum
validates :package, presence: true
validate :rubygems_package_type
private
def rubygems_package_type
unless package&.rubygems?
errors.add(:base, _('Package type must be RubyGems'))
end
end
end
end
end

View File

@ -59,13 +59,13 @@
%li.divider
- if user.can_be_removed?
%li
%button.delete-user-button.btn.btn-default-tertiary.text-danger{ data: { 'gl-modal-action': 'delete',
%button.js-delete-user-modal-button.btn.btn-default-tertiary.text-danger{ data: { 'gl-modal-action': 'delete',
delete_user_url: admin_user_path(user),
block_user_url: block_admin_user_path(user),
username: sanitize_name(user.name) } }
= s_('AdminUsers|Delete user')
%li
%button.delete-user-button.btn.btn-default-tertiary.text-danger{ data: { 'gl-modal-action': 'delete-with-contributions',
%button.js-delete-user-modal-button.btn.btn-default-tertiary.text-danger{ data: { 'gl-modal-action': 'delete-with-contributions',
delete_user_url: admin_user_path(user, hard_delete: true),
block_user_url: block_admin_user_path(user),
username: sanitize_name(user.name) } }

View File

@ -205,7 +205,7 @@
%p Deleting a user has the following effects:
= render 'users/deletion_guidance', user: @user
%br
%button.delete-user-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete',
%button.js-delete-user-modal-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete',
delete_user_url: admin_user_path(@user),
block_user_url: block_admin_user_path(@user),
username: sanitize_name(@user.name) } }
@ -235,7 +235,7 @@
the user, and projects in them, will also be removed. Commits
to other projects are unaffected.
%br
%button.delete-user-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete-with-contributions',
%button.js-delete-user-modal-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete-with-contributions',
delete_user_url: admin_user_path(@user, hard_delete: true),
block_user_url: block_admin_user_path(@user),
username: @user.name } }

View File

@ -0,0 +1,5 @@
---
title: Update max number of NuGet packages returned
merge_request: 52265
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Add rubygems metadata table
merge_request: 52639
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Fix missing known usage data event
merge_request: 53729
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Move the sub-section to gl-card in advanced search settings in admin
merge_request: 52585
author: Yogi (@yo)
type: changed

View File

@ -0,0 +1,5 @@
---
title: Fix misalignment of assignee dropdown checkmark
merge_request: 53664
author:
type: fixed

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddRubygemsMaxFileSizeToPlanLimits < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
add_column :plan_limits, :rubygems_max_file_size, :bigint, default: 3.gigabytes, null: false
end
end

View File

@ -0,0 +1,69 @@
# frozen_string_literal: true
class CreatePackagesRubygemsMetadata < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
create_table_with_constraints :packages_rubygems_metadata, id: false do |t|
t.timestamps_with_timezone
t.references :package, primary_key: true, index: false, default: nil, null: false, foreign_key: { to_table: :packages_packages, on_delete: :cascade }, type: :bigint
t.text :authors
t.text :files
t.text :summary
t.text :description
t.text :email
t.text :homepage
t.text :licenses
t.text :metadata
t.text :author
t.text :bindir
t.text :cert_chain
t.text :executables
t.text :extensions
t.text :extra_rdoc_files
t.text :platform
t.text :post_install_message
t.text :rdoc_options
t.text :require_paths
t.text :required_ruby_version
t.text :required_rubygems_version
t.text :requirements
t.text :rubygems_version
t.text :signing_key
t.text_limit :authors, 255
t.text_limit :files, 255
t.text_limit :summary, 1024
t.text_limit :description, 1024
t.text_limit :email, 255
t.text_limit :homepage, 255
t.text_limit :licenses, 255
t.text_limit :metadata, 255
t.text_limit :author, 255
t.text_limit :bindir, 255
t.text_limit :cert_chain, 255
t.text_limit :executables, 255
t.text_limit :extensions, 255
t.text_limit :extra_rdoc_files, 255
t.text_limit :platform, 255
t.text_limit :post_install_message, 255
t.text_limit :rdoc_options, 255
t.text_limit :require_paths, 255
t.text_limit :required_ruby_version, 255
t.text_limit :required_rubygems_version, 255
t.text_limit :requirements, 255
t.text_limit :rubygems_version, 255
t.text_limit :signing_key, 255
end
end
def down
drop_table :packages_rubygems_metadata
end
end

View File

@ -0,0 +1 @@
4105ae45742c2eda67fe5c54256732e55555ab7832e4cbf0fcb041599c23bd29

View File

@ -0,0 +1 @@
ec6832ba26fca8d8427383cd0189765191a0a7f17bb78d61b900c5b541d5725e

View File

@ -15256,6 +15256,58 @@ CREATE TABLE packages_pypi_metadata (
CONSTRAINT check_379019d5da CHECK ((char_length(required_python) <= 255))
);
CREATE TABLE packages_rubygems_metadata (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
package_id bigint NOT NULL,
authors text,
files text,
summary text,
description text,
email text,
homepage text,
licenses text,
metadata text,
author text,
bindir text,
cert_chain text,
executables text,
extensions text,
extra_rdoc_files text,
platform text,
post_install_message text,
rdoc_options text,
require_paths text,
required_ruby_version text,
required_rubygems_version text,
requirements text,
rubygems_version text,
signing_key text,
CONSTRAINT check_0154a18c82 CHECK ((char_length(description) <= 1024)),
CONSTRAINT check_22814c771b CHECK ((char_length(email) <= 255)),
CONSTRAINT check_242293030e CHECK ((char_length(extensions) <= 255)),
CONSTRAINT check_27619a7922 CHECK ((char_length(rubygems_version) <= 255)),
CONSTRAINT check_3d1b6f3a39 CHECK ((char_length(post_install_message) <= 255)),
CONSTRAINT check_545f7606f9 CHECK ((char_length(required_rubygems_version) <= 255)),
CONSTRAINT check_5988451714 CHECK ((char_length(executables) <= 255)),
CONSTRAINT check_5f9c84ea17 CHECK ((char_length(platform) <= 255)),
CONSTRAINT check_64f1cecf05 CHECK ((char_length(requirements) <= 255)),
CONSTRAINT check_6ac7043c50 CHECK ((char_length(extra_rdoc_files) <= 255)),
CONSTRAINT check_6ff3abe325 CHECK ((char_length(cert_chain) <= 255)),
CONSTRAINT check_7cb01436df CHECK ((char_length(licenses) <= 255)),
CONSTRAINT check_8be21d92e7 CHECK ((char_length(summary) <= 1024)),
CONSTRAINT check_946cb96acb CHECK ((char_length(homepage) <= 255)),
CONSTRAINT check_9824fc9efc CHECK ((char_length(bindir) <= 255)),
CONSTRAINT check_994b68eb64 CHECK ((char_length(authors) <= 255)),
CONSTRAINT check_9d42fa48ae CHECK ((char_length(signing_key) <= 255)),
CONSTRAINT check_b0f4f8c853 CHECK ((char_length(files) <= 255)),
CONSTRAINT check_b7b296b420 CHECK ((char_length(author) <= 255)),
CONSTRAINT check_bf16b21a47 CHECK ((char_length(rdoc_options) <= 255)),
CONSTRAINT check_ca641a3354 CHECK ((char_length(required_ruby_version) <= 255)),
CONSTRAINT check_ea02f4800f CHECK ((char_length(metadata) <= 255)),
CONSTRAINT check_f76bad1a9a CHECK ((char_length(require_paths) <= 255))
);
CREATE TABLE packages_tags (
id bigint NOT NULL,
package_id integer NOT NULL,
@ -15467,7 +15519,8 @@ CREATE TABLE plan_limits (
project_feature_flags integer DEFAULT 200 NOT NULL,
ci_max_artifact_size_api_fuzzing integer DEFAULT 0 NOT NULL,
ci_pipeline_deployments integer DEFAULT 500 NOT NULL,
pull_mirror_interval_seconds integer DEFAULT 300 NOT NULL
pull_mirror_interval_seconds integer DEFAULT 300 NOT NULL,
rubygems_max_file_size bigint DEFAULT '3221225472'::bigint NOT NULL
);
CREATE SEQUENCE plan_limits_id_seq
@ -20511,6 +20564,9 @@ ALTER TABLE ONLY packages_packages
ALTER TABLE ONLY packages_pypi_metadata
ADD CONSTRAINT packages_pypi_metadata_pkey PRIMARY KEY (package_id);
ALTER TABLE ONLY packages_rubygems_metadata
ADD CONSTRAINT packages_rubygems_metadata_pkey PRIMARY KEY (package_id);
ALTER TABLE ONLY packages_tags
ADD CONSTRAINT packages_tags_pkey PRIMARY KEY (id);
@ -25523,6 +25579,9 @@ ALTER TABLE ONLY scim_identities
ALTER TABLE ONLY packages_debian_project_distributions
ADD CONSTRAINT fk_rails_94b95e1f84 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE ONLY packages_rubygems_metadata
ADD CONSTRAINT fk_rails_95a3f5ce78 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_pypi_metadata
ADD CONSTRAINT fk_rails_9698717cdd FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;

View File

@ -612,3 +612,7 @@ Plan.default.actual_limits.update!(generic_packages_max_file_size: 100.megabytes
```
Set the limit to `0` to allow any file size.
### Package versions returned
When asking for versions of a given NuGet package name, the GitLab Package Registry returns a maximum of 300 versions.

View File

@ -17829,6 +17829,11 @@ enum PackageTypeEnum {
Packages from the PyPI package manager
"""
PYPI
"""
Packages from the Rubygems package manager
"""
RUBYGEMS
}
"""

View File

@ -52437,6 +52437,12 @@
"description": "Packages from the Debian package manager",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "RUBYGEMS",
"description": "Packages from the Rubygems package manager",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null

View File

@ -5138,6 +5138,7 @@ Rotation length unit of an on-call rotation.
| `NPM` | Packages from the NPM package manager |
| `NUGET` | Packages from the Nuget package manager |
| `PYPI` | Packages from the PyPI package manager |
| `RUBYGEMS` | Packages from the Rubygems package manager |
### PipelineConfigSourceEnum

View File

@ -294,6 +294,7 @@ See [database guidelines](database/index.md).
- [Compatibility with multiple versions of the application running at the same time](multi_version_compatibility.md)
- [Features inside `.gitlab/`](features_inside_dot_gitlab.md)
- [Dashboards for stage groups](stage_group_dashboards.md)
- [Preventing transient bugs](transient/prevention-patterns.md)
## Other GitLab Development Kit (GDK) guides

View File

@ -328,68 +328,51 @@ with GitLab 11.4. Meaning, it's available only with `/help` from GitLab
### Linking to `/help`
When you're building a new feature, you may need to link the documentation
from GitLab, the application. This is normally done in files inside the
`app/views/` directory with the help of the `help_page_path` helper method.
When you're building a new feature, you may need to link to the documentation
from the GitLab application. This is normally done in files inside the
`app/views/` directory, with the help of the `help_page_path` helper method.
In its simplest form, the HAML code to generate a link to the `/help` page is:
The `help_page_path` contains the path to the document you want to link to,
with the following conventions:
```haml
= link_to 'Help page', help_page_path('user/permissions')
```
- It's relative to the `doc/` directory in the GitLab repository.
- It omits the `.md` extension.
- It doesn't end with a slash (`/`).
The `help_page_path` contains the path to the document you want to link to with
the following conventions:
The help text follows the [Pajamas guidelines](https://design.gitlab.com/usability/helping-users/#formatting-help-content).
- it is relative to the `doc/` directory in the GitLab repository
- the `.md` extension must be omitted
- it must not end with a slash (`/`)
Use the following special cases depending on the context, ensuring all links
are inside `_()` so they can be translated:
Below are some special cases where should be used depending on the context.
You can combine one or more of the following:
- Linking to a doc page. In its most basic form, the HAML code to generate a
link to the `/help` page is:
1. **Linking to an anchor link.** Use `anchor` as part of the `help_page_path`
method:
```haml
= link_to _('Learn more.'), help_page_path('user/permissions'), target: '_blank', rel: 'noopener noreferrer'
```
```haml
= link_to 'Help page', help_page_path('user/permissions', anchor: 'anchor-link')
```
- Linking to an anchor link. Use `anchor` as part of the `help_page_path`
method:
1. **Opening links in a new tab.** This should be the default behavior:
```haml
= link_to _('Learn more.'), help_page_path('user/permissions', anchor: 'anchor-link'), target: '_blank', rel: 'noopener noreferrer'
```
```haml
= link_to 'Help page', help_page_path('user/permissions'), target: '_blank'
```
- Using links inline of some text. First, define the link, and then use it. In
this example, `link_start` is the name of the variable that contains the
link:
1. **Using a question icon.** Usually used in settings where a long
description cannot be used, like near checkboxes. You can basically use
any GitLab SVG icon, but prefer the `question-o`:
```haml
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/permissions') }
%p= _("This is a text describing the option/feature in a sentence. %{link_start}Learn more.%{link_end}").html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
```
```haml
= link_to sprite_icon('question-o'), help_page_path('user/permissions')
```
- Using a button link. Useful in places where text would be out of context with
the rest of the page layout:
1. **Using a button link.** Useful in places where text would be out of context
with the rest of the page layout:
```haml
= link_to 'Help page', help_page_path('user/permissions'), class: 'btn btn-info'
```
1. **Using links inline of some text.**
```haml
Description to #{link_to 'Help page', help_page_path('user/permissions')}.
```
1. **Adding a period at the end of the sentence.** Useful when you don't want
the period to be part of the link:
```haml
= succeed '.' do
Learn more in the
= link_to 'Help page', help_page_path('user/permissions')
```
```haml
= link_to _('Learn more.'), help_page_path('user/permissions'), class: 'btn btn-info', target: '_blank', rel: 'noopener noreferrer'
```
#### Linking to `/help` in JavaScript

View File

@ -0,0 +1,132 @@
---
stage: none
group: unassigned
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
---
# Preventing Transient Bugs
This page will cover architectural patterns and tips for developers to follow to prevent [transient bugs.](https://about.gitlab.com/handbook/engineering/quality/issue-triage/#transient-bugs)
## Frontend
### Don't rely on response order
When working with multiple requests, it's easy to assume the order of the responses will match the order in which they are triggered.
That's not always the case and can cause bugs that only happen if the order is switched.
**Example:**
- `diffs_metadata.json` (lighter)
- `diffs_batch.json` (heavier)
If your feature requires data from both, ensure that the two have finished loading before working on it.
### Simulate slower connections when testing manually
Add a network condition template to your browser's dev tools to enable you to toggle between a slow and a fast connection.
**Example:**
- Turtle:
- Down: 50kb/s
- Up: 20kb/s
- Latency: 10000ms
### Collapsed elements
When setting event listeners, if not possible to use event delegation, ensure all relevant event listeners are set for expanded content.
Including when that expanded content is:
- **Invisible** (`display: none;`). Some JavaScript requires the element to be visible to work properly (eg.: when taking measurements).
- **Dynamic content** (AJAX/DOM manipulation).
### Using assertions to detect transient bugs caused by unmet conditions
Transient bugs happen in the context of code that executes under the assumption
that the applications state meets one or more conditions. We may write a feature
that assumes a server-side API response always include a group of attributes or that
an operation only executes when the application has successfully transitioned to a new
state.
Transient bugs are difficult to debug because there isnt any mechanism that alerts
the user or the developer about unsatisfied conditions. These conditions are usually
not expressed explicitly in the code. A useful debugging technique for such situations
is placing assertions to make any assumption explicit. They can help detect the cause
which unmet condition causes the bug.
#### Asserting pre-conditions on state mutations
A common scenario that leads to transient bugs is when there is a polling service
that should mutate state only if a user operation is completed. We can use
assertions to make this pre-condition explicit:
```javascript
// This action is called by a polling service. It assumes that all pre-conditions
// are satisfied by the time the action is dispatched.
export const updateMergeableStatus = ({ commit }, payload) => {
commit(types.SET_MERGEABLE_STATUS, payload);
};
// We can make any pre-condition explicit by adding an assertion
export const updateMergeableStatus = ({ state, commit }, payload) => {
console.assert(
state.isResolvingDiscussion === true,
'Resolve discussion request must be completed before updating mergeable status'
);
commit(types.SET_MERGEABLE_STATUS, payload);
};
```
#### Asserting API contracts
Another useful way of using assertions is to detect if the response payload returned
by the server-side endpoint satisfies the API contract.
#### Related reading
[Debug it!](https://pragprog.com/titles/pbdp/debug-it/) explores techniques to diagnose
and fix non-determinstic bugs and write software that is easier to debug.
## Backend
### Sidekiq jobs with locks
When dealing with asynchronous work via Sidekiq, it is possible to have 2 jobs with the same arguments
getting worked on at the same time. If not handled correctly, this can result in an outdated or inaccurate state.
For instance, consider a worker that updates a state of an object. Before the worker updates the state
(for example, `#update_state`) of the object, it needs to check what the appropriate state should be
(for example, `#check_state`).
When there are 2 jobs being worked on at the same time, it is possible that the order of operations will go like:
1. (Worker A) Calls `#check_state`
1. (Worker B) Calls `#check_state`
1. (Worker B) Calls `#update_state`
1. (Worker A) Calls `#update_state`
In this example, `Worker B` is meant to set the updated status. But `Worker A` calls `#update_state` a little too late.
This can be avoided by utilizing either database locks or `Gitlab::ExclusiveLease`. This way, jobs will be
worked on one at a time. This also allows them to be marked as [idempotent](../sidekiq_style_guide.md#idempotent-jobs).
### Retry mechanism handling
There are times that an object/record will be on a failed state which can be rechecked.
If an object is in a state that can be rechecked, ensure that appropriate messaging is shown to the user
so they know what to do. Also, make sure that the retry functionality will be able to reset the state
correctly when triggered.
### Error Logging
Error logging doesn't necessarily directly prevents transient bugs but it can help to debug them.
When coding, sometimes we expect some exceptions to be raised and we rescue them.
Logging whenever we rescue an error helps in case it's causing transient bugs that a user may see.
While investigating a bug report, it may require the engineer to look into logs of when it happened.
Seeing an error being logged can be a signal of something that went wrong which can be handled differently.

View File

@ -6,8 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Alert integrations **(FREE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13203) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.4.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/42640) to [GitLab Core](https://about.gitlab.com/pricing/) in 12.8.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13203) in GitLab Ultimate 12.4.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/42640) to GitLab Free in 12.8.
GitLab can accept alerts from any source via a webhook receiver. This can be configured
generically or, in GitLab versions 13.1 and greater, you can configure
@ -16,7 +16,7 @@ to use this endpoint.
## Integrations list
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/245331) in [GitLab Core](https://about.gitlab.com/pricing/) 13.5.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/245331) in GitLab Free 13.5.
With Maintainer or higher [permissions](../../user/permissions.md), you can view
the list of configured alerts integrations by navigating to
@ -45,7 +45,7 @@ receive alert payloads in JSON format. You can always
### HTTP Endpoints **PREMIUM**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4442) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.6.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4442) in GitLab Premium 13.6.
In [GitLab Premium](https://about.gitlab.com/pricing/), you can create multiple
unique HTTP endpoints to receive alerts from any external source in JSON format,
@ -140,7 +140,7 @@ Example payload:
## Triggering test alerts
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab Core in 13.2.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab Free in 13.2.
After a [project maintainer or owner](../../user/permissions.md)
configures an integration, you can trigger a test
@ -156,7 +156,7 @@ GitLab displays an error or success message, depending on the outcome of your te
## Automatic grouping of identical alerts **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214557) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214557) in GitLab Premium 13.2.
In GitLab versions 13.2 and greater, GitLab groups alerts based on their
payload. When an incoming alert contains the same payload as another alert
@ -170,7 +170,7 @@ If the existing alert is already `resolved`, GitLab creates a new alert instead.
## Link to your Opsgenie Alerts
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab Premium 13.2.
WARNING:
We are building deeper integration with Opsgenie and other alerting tools through

View File

@ -9,11 +9,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab provides a comprehensive set of features for viewing and managing vulnerabilities:
- Security dashboards: An overview of the security status in your instance, [groups](#group-security-dashboard), and
- Security dashboards: An overview of the security status in your personal [Security Center](#security-center), [groups](#group-security-dashboard), and
[projects](#project-security-dashboard).
- [Vulnerability reports](../vulnerability_report/index.md): Detailed lists of all vulnerabilities for the instance, group, project, or
- [Vulnerability reports](../vulnerability_report/index.md): Detailed lists of all vulnerabilities for the Security Center, group, project, or
pipeline. This is where you triage and manage vulnerabilities.
- [Security Center](#instance-security-center): A dedicated area for vulnerability management at the instance level. This
- [Security Center](#security-center): A dedicated area for personalized vulnerability management. This
includes a security dashboard, vulnerability report, and settings.
You can also drill down into a vulnerability and get extra information on the
@ -111,28 +111,28 @@ vulnerabilities are excluded.
Navigate to the group's [vulnerability report](../vulnerability_report/index.md) to view the vulnerabilities found.
## Instance Security Center
## Security Center
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3426) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.4.
The Security Center is where you manage vulnerabilities for your instance. It displays the
vulnerabilities present in the default branches of all the projects you configure. It includes the
following:
The Security Center is personal space where you manage vulnerabilities across all your projects. It
displays the vulnerabilities present in the default branches of all the projects you configure. It includes
the following:
- The [group security dashboard's](#group-security-dashboard) features.
- A [vulnerability report](../vulnerability_report/index.md).
- A dedicated settings area to configure which projects to display.
![Instance Security Dashboard with projects](img/instance_security_dashboard_v13_4.png)
![Security Center Dashboard with projects](img/security_center_dashboard_v13_4.png)
You can access the Instance Security Center from the menu
You can access the Security Center from the menu
bar at the top of the page. Under **More**, select **Security**.
![Instance Security Center navigation link](img/instance_security_dashboard_link_v12_4.png)
![Security Center navigation link](img/security_center_dashboard_link_v12_4.png)
The dashboard and vulnerability report are empty before you add projects.
![Uninitialized Instance Security Center](img/instance_security_dashboard_empty_v13_4.png)
![Uninitialized Security Center](img/security_center_dashboard_empty_v13_4.png)
### Adding projects to the Security Center
@ -142,7 +142,7 @@ To add projects to the Security Center:
1. Search for and add one or more projects using the **Search your projects** field.
1. Click the **Add projects** button.
![Adding projects to Instance Security Center](img/instance_security_center_settings_v13_4.png)
![Adding projects to Security Center](img/security_center_settings_v13_4.png)
After you add projects, the security dashboard and vulnerability report display the vulnerabilities
found in those projects' default branches.

View File

@ -603,6 +603,11 @@
redis_slot: ci_templates
aggregation: weekly
feature_flag: usage_data_track_ci_templates_unique_projects
- name: p_ci_templates_aws_deploy_ecs
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
feature_flag: usage_data_track_ci_templates_unique_projects
- name: p_ci_templates_auto_devops_build
category: ci_templates
redis_slot: ci_templates

View File

@ -20862,6 +20862,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
msgid "Package type must be RubyGems"
msgstr ""
msgid "PackageRegistry|%{boldStart}Allow duplicates%{boldEnd} - Packages with the same name and version are accepted."
msgstr ""

View File

@ -21,6 +21,23 @@ FactoryBot.define do
end
end
factory :rubygems_package do
sequence(:name) { |n| "my_gem_#{n}" }
sequence(:version) { |n| "1.#{n}" }
package_type { :rubygems }
after :create do |package|
create :package_file, :gem, package: package
create :package_file, :gemspec, package: package
end
trait(:with_metadatum) do
after :build do |pkg|
pkg.rubygems_metadatum = build(:rubygems_metadatum)
end
end
end
factory :debian_package do
sequence(:name) { |n| "package-#{n}" }
sequence(:version) { |n| "1.0-#{n}" }

View File

@ -221,6 +221,22 @@ FactoryBot.define do
size { 300.kilobytes }
end
trait(:gem) do
package
file_fixture { 'spec/fixtures/packages/rubygems/package-0.0.1.gem' }
file_name { 'package-0.0.1.gem' }
file_sha1 { '5fe852b2a6abd96c22c11fa1ff2fb19d9ce58b57' }
size { 4.kilobytes }
end
trait(:gemspec) do
package
file_fixture { 'spec/fixtures/packages/rubygems/package.gemspec' }
file_name { 'package.gemspec' }
file_sha1 { '5fe852b2a6abd96c22c11fa1ff2fb19d9ce58b57' }
size { 242.bytes }
end
trait(:pypi) do
package
file_fixture { 'spec/fixtures/packages/pypi/sample-project.tar.gz' }

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
FactoryBot.define do
factory :rubygems_metadatum, class: 'Packages::Rubygems::Metadatum' do
package { association(:rubygems_package) }
authors { FFaker::Name.name }
email { FFaker::Internet.email }
end
end

Binary file not shown.

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
Gem::Specification.new do |s|
s.name = %q{package}
s.authors = ["Tanuki Steve"]
s.version = "0.0.1"
s.date = %q{2011-09-29}
s.summary = %q{package is the best}
s.files = [
"lib/package.rb"
]
s.required_ruby_version = '>= 2.7.0'
s.rubygems_version = '>= 1.8.11'
s.require_paths = ["lib"]
end

View File

@ -3,6 +3,8 @@ import UserModalManager from '~/pages/admin/users/components/user_modal_manager.
import ModalStub from './stubs/modal_stub';
describe('Users admin page Modal Manager', () => {
let wrapper;
const modalConfiguration = {
action1: {
title: 'action1',
@ -14,11 +16,12 @@ describe('Users admin page Modal Manager', () => {
},
};
let wrapper;
const findModal = () => wrapper.find({ ref: 'modal' });
const createComponent = (props = {}) => {
wrapper = mount(UserModalManager, {
propsData: {
selector: '.js-delete-user-modal-button',
modalConfiguration,
csrfToken: 'dummyCSRF',
...props,
@ -37,7 +40,7 @@ describe('Users admin page Modal Manager', () => {
describe('render behavior', () => {
it('does not renders modal when initialized', () => {
createComponent();
expect(wrapper.find({ ref: 'modal' }).exists()).toBeFalsy();
expect(findModal().exists()).toBeFalsy();
});
it('throws if action has no proper configuration', () => {
@ -55,7 +58,7 @@ describe('Users admin page Modal Manager', () => {
});
return wrapper.vm.$nextTick().then(() => {
const modal = wrapper.find({ ref: 'modal' });
const modal = findModal();
expect(modal.exists()).toBeTruthy();
expect(modal.vm.$attrs.csrfToken).toEqual('dummyCSRF');
expect(modal.vm.$attrs.extraProp).toEqual('extraPropValue');
@ -64,68 +67,60 @@ describe('Users admin page Modal Manager', () => {
});
});
describe('global listener', () => {
beforeEach(() => {
jest.spyOn(document, 'addEventListener');
jest.spyOn(document, 'removeEventListener');
});
afterAll(() => {
jest.restoreAllMocks();
});
it('registers global listener on mount', () => {
createComponent();
expect(document.addEventListener).toHaveBeenCalledWith('click', expect.any(Function));
});
it('removes global listener on destroy', () => {
createComponent();
wrapper.destroy();
expect(document.removeEventListener).toHaveBeenCalledWith('click', expect.any(Function));
});
});
describe('click handling', () => {
let node;
let button;
let button2;
const createButtons = () => {
button = document.createElement('button');
button2 = document.createElement('button');
button.setAttribute('class', 'js-delete-user-modal-button');
button.setAttribute('data-username', 'foo');
button.setAttribute('data-gl-modal-action', 'action1');
button.setAttribute('data-block-user-url', '/block');
button.setAttribute('data-delete-user-url', '/delete');
document.body.appendChild(button);
document.body.appendChild(button2);
};
const removeButtons = () => {
button.remove();
button = null;
button2.remove();
button2 = null;
};
beforeEach(() => {
node = document.createElement('div');
document.body.appendChild(node);
createButtons();
createComponent();
});
afterEach(() => {
node.remove();
node = null;
removeButtons();
});
it('ignores wrong clicks', () => {
createComponent();
const event = new window.MouseEvent('click', {
bubbles: true,
cancellable: true,
});
jest.spyOn(event, 'preventDefault');
node.dispatchEvent(event);
expect(event.preventDefault).not.toHaveBeenCalled();
it('renders the modal when the button is clicked', async () => {
button.click();
await wrapper.vm.$nextTick();
expect(findModal().exists()).toBe(true);
});
it('captures click with glModalAction', () => {
createComponent();
node.dataset.glModalAction = 'action1';
const event = new window.MouseEvent('click', {
bubbles: true,
cancellable: true,
});
jest.spyOn(event, 'preventDefault');
node.dispatchEvent(event);
it('does not render the modal when a misconfigured button is clicked', async () => {
button.removeAttribute('data-gl-modal-action');
button.click();
expect(event.preventDefault).toHaveBeenCalled();
return wrapper.vm.$nextTick().then(() => {
const modal = wrapper.find({ ref: 'modal' });
expect(modal.exists()).toBeTruthy();
expect(modal.vm.showWasCalled).toBeTruthy();
});
await wrapper.vm.$nextTick();
expect(findModal().exists()).toBe(false);
});
it('does not render the modal when a button without the selector class is clicked', async () => {
button2.click();
await wrapper.vm.$nextTick();
expect(findModal().exists()).toBe(false);
});
});
});

View File

@ -4,6 +4,6 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['PackageTypeEnum'] do
it 'exposes all package types' do
expect(described_class.values.keys).to contain_exactly(*%w[MAVEN NPM CONAN NUGET PYPI COMPOSER GENERIC GOLANG DEBIAN])
expect(described_class.values.keys).to contain_exactly(*%w[MAVEN NPM CONAN NUGET PYPI COMPOSER GENERIC GOLANG DEBIAN RUBYGEMS])
end
end

View File

@ -17,6 +17,7 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to have_one(:debian_publication).inverse_of(:package).class_name('Packages::Debian::Publication') }
it { is_expected.to have_one(:debian_distribution).through(:debian_publication).source(:distribution).inverse_of(:packages).class_name('Packages::Debian::ProjectDistribution') }
it { is_expected.to have_one(:nuget_metadatum).inverse_of(:package) }
it { is_expected.to have_one(:rubygems_metadatum).inverse_of(:package) }
end
describe '.with_composer_target' do

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Packages::Rubygems::Metadatum, type: :model do
describe 'relationships' do
it { is_expected.to belong_to(:package) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:package) }
describe '#rubygems_package_type' do
it 'will not allow a package with a different package_type' do
package = build('conan_package')
rubygems_metadatum = build('rubygems_metadatum', package: package)
expect(rubygems_metadatum).not_to be_valid
expect(rubygems_metadatum.errors.to_a).to include('Package type must be RubyGems')
end
end
end
end

View File

@ -7,7 +7,11 @@
RSpec.shared_examples 'tracking unique hll events' do |feature_flag|
it 'tracks unique event' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with(target_id, values: expected_type)
expect(Gitlab::UsageDataCounters::HLLRedisCounter).to(
receive(:track_event)
.with(target_id, values: expected_type)
.and_call_original # we call original to trigger additional validations; otherwise the method is stubbed
)
request
end

View File

@ -190,6 +190,7 @@ RSpec.shared_examples 'filters on each package_type' do |is_project: false|
let_it_be(:package7) { create(:generic_package, project: project) }
let_it_be(:package8) { create(:golang_package, project: project) }
let_it_be(:package9) { create(:debian_package, project: project) }
let_it_be(:package9) { create(:rubygems_package, project: project) }
Packages::Package.package_types.keys.each do |package_type|
context "for package type #{package_type}" do