summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-10 00:09:33 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-10 00:09:33 +0000
commit5a120c32fda1c88e38bbce056d6f30f4a2f41bc6 (patch)
tree8f5ef3c06e5f2f8707da5d32803d73fe10824ea5
parentb5944525b015e4efb4cd2c1d09ec37566d7691a0 (diff)
downloadgitlab-ce-5a120c32fda1c88e38bbce056d6f30f4a2f41bc6.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/pages/admin/users/components/user_modal_manager.vue30
-rw-r--r--app/assets/javascripts/pages/admin/users/index.js2
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss2
-rw-r--r--app/finders/packages/nuget/package_finder.rb2
-rw-r--r--app/models/packages/package.rb5
-rw-r--r--app/models/packages/rubygems/metadatum.rb24
-rw-r--r--app/views/admin/users/_user.html.haml4
-rw-r--r--app/views/admin/users/show.html.haml4
-rw-r--r--changelogs/unreleased/241376-max-nuget-packages-returned.yml5
-rw-r--r--changelogs/unreleased/299263-rubygems-metadata-migration.yml5
-rw-r--r--changelogs/unreleased/add-missing-known-event-ecs-deploy-template.yml5
-rw-r--r--changelogs/unreleased/gl-card-advanced-search.yml5
-rw-r--r--changelogs/unreleased/peterhegman-fix-assignee-dropdown-checkmark.yml5
-rw-r--r--db/migrate/20210126233608_add_rubygems_max_file_size_to_plan_limits.rb9
-rw-r--r--db/migrate/20210203221631_create_packages_rubygems_metadata.rb69
-rw-r--r--db/schema_migrations/202101262336081
-rw-r--r--db/schema_migrations/202102032216311
-rw-r--r--db/structure.sql61
-rw-r--r--doc/administration/instance_limits.md4
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql5
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json6
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/documentation/index.md83
-rw-r--r--doc/development/transient/prevention-patterns.md132
-rw-r--r--doc/operations/incident_management/integrations.md14
-rw-r--r--doc/user/application_security/security_dashboard/img/security_center_dashboard_empty_v13_4.png (renamed from doc/user/application_security/security_dashboard/img/instance_security_dashboard_empty_v13_4.png)bin13264 -> 13264 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/security_center_dashboard_link_v12_4.png (renamed from doc/user/application_security/security_dashboard/img/instance_security_dashboard_link_v12_4.png)bin7921 -> 7921 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/security_center_dashboard_v13_4.png (renamed from doc/user/application_security/security_dashboard/img/instance_security_dashboard_v13_4.png)bin29797 -> 29797 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/security_center_settings_v13_4.png (renamed from doc/user/application_security/security_dashboard/img/instance_security_center_settings_v13_4.png)bin30034 -> 30034 bytes
-rw-r--r--doc/user/application_security/security_dashboard/index.md24
-rw-r--r--lib/gitlab/usage_data_counters/known_events/common.yml5
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/factories/packages.rb17
-rw-r--r--spec/factories/packages/package_file.rb16
-rw-r--r--spec/factories/packages/rubygems/metadata.rb9
-rw-r--r--spec/fixtures/packages/rubygems/package-0.0.1.gembin0 -> 4096 bytes
-rw-r--r--spec/fixtures/packages/rubygems/package.gemspec15
-rw-r--r--spec/frontend/pages/admin/users/components/user_modal_manager_spec.js97
-rw-r--r--spec/graphql/types/packages/package_type_enum_spec.rb2
-rw-r--r--spec/models/packages/package_spec.rb1
-rw-r--r--spec/models/packages/rubygems/metadatum_spec.rb22
-rw-r--r--spec/support/shared_examples/controllers/unique_hll_events_examples.rb6
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb1
44 files changed, 561 insertions, 142 deletions
diff --git a/app/assets/javascripts/pages/admin/users/components/user_modal_manager.vue b/app/assets/javascripts/pages/admin/users/components/user_modal_manager.vue
index 24c9fa4cb3f..1dfea3f1e7b 100644
--- a/app/assets/javascripts/pages/admin/users/components/user_modal_manager.vue
+++ b/app/assets/javascripts/pages/admin/users/components/user_modal_manager.vue
@@ -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;
diff --git a/app/assets/javascripts/pages/admin/users/index.js b/app/assets/javascripts/pages/admin/users/index.js
index 1fd838e704c..ae5db5f5bdc 100644
--- a/app/assets/javascripts/pages/admin/users/index.js
+++ b/app/assets/javascripts/pages/admin/users/index.js
@@ -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,
},
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 8a9a632003a..f6162f65e21 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -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;
}
}
}
diff --git a/app/finders/packages/nuget/package_finder.rb b/app/finders/packages/nuget/package_finder.rb
index 8f585f045a1..2f66bd145ee 100644
--- a/app/finders/packages/nuget/package_finder.rb
+++ b/app/finders/packages/nuget/package_finder.rb
@@ -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
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index 4200f68d8fd..7bc74e0db74 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -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)) }
diff --git a/app/models/packages/rubygems/metadatum.rb b/app/models/packages/rubygems/metadatum.rb
new file mode 100644
index 00000000000..42db1f3defc
--- /dev/null
+++ b/app/models/packages/rubygems/metadatum.rb
@@ -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
diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml
index 31fd3aea94d..224a3cea28d 100644
--- a/app/views/admin/users/_user.html.haml
+++ b/app/views/admin/users/_user.html.haml
@@ -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) } }
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 380348f9a98..c7ec3ab66d7 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -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 } }
diff --git a/changelogs/unreleased/241376-max-nuget-packages-returned.yml b/changelogs/unreleased/241376-max-nuget-packages-returned.yml
new file mode 100644
index 00000000000..82ad553278c
--- /dev/null
+++ b/changelogs/unreleased/241376-max-nuget-packages-returned.yml
@@ -0,0 +1,5 @@
+---
+title: Update max number of NuGet packages returned
+merge_request: 52265
+author:
+type: fixed
diff --git a/changelogs/unreleased/299263-rubygems-metadata-migration.yml b/changelogs/unreleased/299263-rubygems-metadata-migration.yml
new file mode 100644
index 00000000000..be5842a17d6
--- /dev/null
+++ b/changelogs/unreleased/299263-rubygems-metadata-migration.yml
@@ -0,0 +1,5 @@
+---
+title: Add rubygems metadata table
+merge_request: 52639
+author:
+type: added
diff --git a/changelogs/unreleased/add-missing-known-event-ecs-deploy-template.yml b/changelogs/unreleased/add-missing-known-event-ecs-deploy-template.yml
new file mode 100644
index 00000000000..cbb5b48162f
--- /dev/null
+++ b/changelogs/unreleased/add-missing-known-event-ecs-deploy-template.yml
@@ -0,0 +1,5 @@
+---
+title: Fix missing known usage data event
+merge_request: 53729
+author:
+type: fixed
diff --git a/changelogs/unreleased/gl-card-advanced-search.yml b/changelogs/unreleased/gl-card-advanced-search.yml
new file mode 100644
index 00000000000..79dc47cd062
--- /dev/null
+++ b/changelogs/unreleased/gl-card-advanced-search.yml
@@ -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
diff --git a/changelogs/unreleased/peterhegman-fix-assignee-dropdown-checkmark.yml b/changelogs/unreleased/peterhegman-fix-assignee-dropdown-checkmark.yml
new file mode 100644
index 00000000000..eb29e0828ae
--- /dev/null
+++ b/changelogs/unreleased/peterhegman-fix-assignee-dropdown-checkmark.yml
@@ -0,0 +1,5 @@
+---
+title: Fix misalignment of assignee dropdown checkmark
+merge_request: 53664
+author:
+type: fixed
diff --git a/db/migrate/20210126233608_add_rubygems_max_file_size_to_plan_limits.rb b/db/migrate/20210126233608_add_rubygems_max_file_size_to_plan_limits.rb
new file mode 100644
index 00000000000..e0e7e773d17
--- /dev/null
+++ b/db/migrate/20210126233608_add_rubygems_max_file_size_to_plan_limits.rb
@@ -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
diff --git a/db/migrate/20210203221631_create_packages_rubygems_metadata.rb b/db/migrate/20210203221631_create_packages_rubygems_metadata.rb
new file mode 100644
index 00000000000..f4ad5abf7e5
--- /dev/null
+++ b/db/migrate/20210203221631_create_packages_rubygems_metadata.rb
@@ -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
diff --git a/db/schema_migrations/20210126233608 b/db/schema_migrations/20210126233608
new file mode 100644
index 00000000000..adab3a3f365
--- /dev/null
+++ b/db/schema_migrations/20210126233608
@@ -0,0 +1 @@
+4105ae45742c2eda67fe5c54256732e55555ab7832e4cbf0fcb041599c23bd29 \ No newline at end of file
diff --git a/db/schema_migrations/20210203221631 b/db/schema_migrations/20210203221631
new file mode 100644
index 00000000000..ed9efcd2b5f
--- /dev/null
+++ b/db/schema_migrations/20210203221631
@@ -0,0 +1 @@
+ec6832ba26fca8d8427383cd0189765191a0a7f17bb78d61b900c5b541d5725e \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 8c49262f1e3..2f10967b907 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -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;
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index 882928a3628..57af1166076 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -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.
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 1e15a20390d..e2f8485ce63 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -17829,6 +17829,11 @@ enum PackageTypeEnum {
Packages from the PyPI package manager
"""
PYPI
+
+ """
+ Packages from the Rubygems package manager
+ """
+ RUBYGEMS
}
"""
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 8435194b1bc..6ff2cab1512 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -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
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index c75671bf797..963b5e9f091 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -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
diff --git a/doc/development/README.md b/doc/development/README.md
index d73acdd9dbc..5db4c5438c4 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -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
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index b878f0fc1ce..6b30a6dab5d 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -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')
-```
-
-The `help_page_path` contains the path to the document you want to link to with
-the following conventions:
-
-- it is relative to the `doc/` directory in the GitLab repository
-- the `.md` extension must be omitted
-- it must not end with a slash (`/`)
-
-Below are some special cases where should be used depending on the context.
-You can combine one or more of the following:
-
-1. **Linking to an anchor link.** Use `anchor` as part of the `help_page_path`
- method:
+- It's relative to the `doc/` directory in the GitLab repository.
+- It omits the `.md` extension.
+- It doesn't end with a slash (`/`).
- ```haml
- = link_to 'Help page', help_page_path('user/permissions', anchor: 'anchor-link')
- ```
+The help text follows the [Pajamas guidelines](https://design.gitlab.com/usability/helping-users/#formatting-help-content).
-1. **Opening links in a new tab.** This should be the default behavior:
+Use the following special cases depending on the context, ensuring all links
+are inside `_()` so they can be translated:
- ```haml
- = link_to 'Help page', help_page_path('user/permissions'), target: '_blank'
- ```
+- Linking to a doc page. In its most basic form, the HAML code to generate a
+ link to the `/help` page is:
-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_to sprite_icon('question-o'), help_page_path('user/permissions')
- ```
+ ```haml
+ = link_to _('Learn more.'), help_page_path('user/permissions'), target: '_blank', rel: 'noopener noreferrer'
+ ```
-1. **Using a button link.** Useful in places where text would be out of context
- with the rest of the page layout:
+- Linking to an anchor link. Use `anchor` as part of the `help_page_path`
+ method:
- ```haml
- = link_to 'Help page', help_page_path('user/permissions'), class: 'btn btn-info'
- ```
+ ```haml
+ = link_to _('Learn more.'), help_page_path('user/permissions', anchor: 'anchor-link'), target: '_blank', rel: 'noopener noreferrer'
+ ```
-1. **Using links inline of some text.**
+- 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:
- ```haml
- Description to #{link_to 'Help page', help_page_path('user/permissions')}.
- ```
+ ```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 }
+ ```
-1. **Adding a period at the end of the sentence.** Useful when you don't want
- the period to be part of the link:
+- Using a button link. Useful in places where text would be out of context with
+ the rest of the page layout:
- ```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
diff --git a/doc/development/transient/prevention-patterns.md b/doc/development/transient/prevention-patterns.md
new file mode 100644
index 00000000000..7231562415b
--- /dev/null
+++ b/doc/development/transient/prevention-patterns.md
@@ -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 application’s 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 isn’t 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.
diff --git a/doc/operations/incident_management/integrations.md b/doc/operations/incident_management/integrations.md
index 2ffe3e7e9fb..1f3a6021588 100644
--- a/doc/operations/incident_management/integrations.md
+++ b/doc/operations/incident_management/integrations.md
@@ -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
diff --git a/doc/user/application_security/security_dashboard/img/instance_security_dashboard_empty_v13_4.png b/doc/user/application_security/security_dashboard/img/security_center_dashboard_empty_v13_4.png
index 5edceb32e5c..5edceb32e5c 100644
--- a/doc/user/application_security/security_dashboard/img/instance_security_dashboard_empty_v13_4.png
+++ b/doc/user/application_security/security_dashboard/img/security_center_dashboard_empty_v13_4.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/instance_security_dashboard_link_v12_4.png b/doc/user/application_security/security_dashboard/img/security_center_dashboard_link_v12_4.png
index e0e80810b08..e0e80810b08 100644
--- a/doc/user/application_security/security_dashboard/img/instance_security_dashboard_link_v12_4.png
+++ b/doc/user/application_security/security_dashboard/img/security_center_dashboard_link_v12_4.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/instance_security_dashboard_v13_4.png b/doc/user/application_security/security_dashboard/img/security_center_dashboard_v13_4.png
index 5379b5c6e5d..5379b5c6e5d 100644
--- a/doc/user/application_security/security_dashboard/img/instance_security_dashboard_v13_4.png
+++ b/doc/user/application_security/security_dashboard/img/security_center_dashboard_v13_4.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/instance_security_center_settings_v13_4.png b/doc/user/application_security/security_dashboard/img/security_center_settings_v13_4.png
index 4223494c294..4223494c294 100644
--- a/doc/user/application_security/security_dashboard/img/instance_security_center_settings_v13_4.png
+++ b/doc/user/application_security/security_dashboard/img/security_center_settings_v13_4.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index 08faf9bf885..b08c19bee47 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -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.
diff --git a/lib/gitlab/usage_data_counters/known_events/common.yml b/lib/gitlab/usage_data_counters/known_events/common.yml
index 1828731a504..5cf61acee9e 100644
--- a/lib/gitlab/usage_data_counters/known_events/common.yml
+++ b/lib/gitlab/usage_data_counters/known_events/common.yml
@@ -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
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a5b531b90c3..5e94b9865c5 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/spec/factories/packages.rb b/spec/factories/packages.rb
index 158c5974c6a..7cf37db340d 100644
--- a/spec/factories/packages.rb
+++ b/spec/factories/packages.rb
@@ -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}" }
diff --git a/spec/factories/packages/package_file.rb b/spec/factories/packages/package_file.rb
index c328c01ec95..6d8b119040e 100644
--- a/spec/factories/packages/package_file.rb
+++ b/spec/factories/packages/package_file.rb
@@ -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' }
diff --git a/spec/factories/packages/rubygems/metadata.rb b/spec/factories/packages/rubygems/metadata.rb
new file mode 100644
index 00000000000..9f03bf80dc3
--- /dev/null
+++ b/spec/factories/packages/rubygems/metadata.rb
@@ -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
diff --git a/spec/fixtures/packages/rubygems/package-0.0.1.gem b/spec/fixtures/packages/rubygems/package-0.0.1.gem
new file mode 100644
index 00000000000..2143ef408ac
--- /dev/null
+++ b/spec/fixtures/packages/rubygems/package-0.0.1.gem
Binary files differ
diff --git a/spec/fixtures/packages/rubygems/package.gemspec b/spec/fixtures/packages/rubygems/package.gemspec
new file mode 100644
index 00000000000..bb87c47f5dc
--- /dev/null
+++ b/spec/fixtures/packages/rubygems/package.gemspec
@@ -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
diff --git a/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js b/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
index 6df2efd624d..3669bc40d7e 100644
--- a/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
+++ b/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
@@ -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', () => {
+ describe('click handling', () => {
+ 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(() => {
- jest.spyOn(document, 'addEventListener');
- jest.spyOn(document, 'removeEventListener');
+ createButtons();
+ createComponent();
});
- afterAll(() => {
- jest.restoreAllMocks();
+ afterEach(() => {
+ removeButtons();
});
- it('registers global listener on mount', () => {
- createComponent();
- expect(document.addEventListener).toHaveBeenCalledWith('click', expect.any(Function));
- });
+ it('renders the modal when the button is clicked', async () => {
+ button.click();
- it('removes global listener on destroy', () => {
- createComponent();
- wrapper.destroy();
- expect(document.removeEventListener).toHaveBeenCalledWith('click', expect.any(Function));
+ await wrapper.vm.$nextTick();
+
+ expect(findModal().exists()).toBe(true);
});
- });
- describe('click handling', () => {
- let node;
+ it('does not render the modal when a misconfigured button is clicked', async () => {
+ button.removeAttribute('data-gl-modal-action');
+ button.click();
- beforeEach(() => {
- node = document.createElement('div');
- document.body.appendChild(node);
- });
+ await wrapper.vm.$nextTick();
- afterEach(() => {
- node.remove();
- node = null;
+ expect(findModal().exists()).toBe(false);
});
- 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('does not render the modal when a button without the selector class is clicked', async () => {
+ button2.click();
- 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);
+ await wrapper.vm.$nextTick();
- expect(event.preventDefault).toHaveBeenCalled();
- return wrapper.vm.$nextTick().then(() => {
- const modal = wrapper.find({ ref: 'modal' });
- expect(modal.exists()).toBeTruthy();
- expect(modal.vm.showWasCalled).toBeTruthy();
- });
+ expect(findModal().exists()).toBe(false);
});
});
});
diff --git a/spec/graphql/types/packages/package_type_enum_spec.rb b/spec/graphql/types/packages/package_type_enum_spec.rb
index 407d5786f65..ccd91485e4b 100644
--- a/spec/graphql/types/packages/package_type_enum_spec.rb
+++ b/spec/graphql/types/packages/package_type_enum_spec.rb
@@ -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
diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb
index b5ff417e610..cdf03b3c958 100644
--- a/spec/models/packages/package_spec.rb
+++ b/spec/models/packages/package_spec.rb
@@ -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
diff --git a/spec/models/packages/rubygems/metadatum_spec.rb b/spec/models/packages/rubygems/metadatum_spec.rb
new file mode 100644
index 00000000000..e99a07c7731
--- /dev/null
+++ b/spec/models/packages/rubygems/metadatum_spec.rb
@@ -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
diff --git a/spec/support/shared_examples/controllers/unique_hll_events_examples.rb b/spec/support/shared_examples/controllers/unique_hll_events_examples.rb
index c5d65743810..3f69923028c 100644
--- a/spec/support/shared_examples/controllers/unique_hll_events_examples.rb
+++ b/spec/support/shared_examples/controllers/unique_hll_events_examples.rb
@@ -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
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index fa307d2a9a6..ab6cd2109cb 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -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