Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-06-07 18:09:56 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-07 18:09:56 +0300
commit79f98200f84590af39cf1af7f57f6e8ba89d2bb6 (patch)
tree289fadec4d3f96a681b3938debaf3800806471ff
parentde8e5077c3671b0b29642faf1b5e562bc4f99453 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/invite_members/components/invite_members_modal.vue3
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js4
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue33
-rw-r--r--app/assets/javascripts/repository/queries/blob_info.query.graphql1
-rw-r--r--app/controllers/application_controller.rb10
-rw-r--r--app/controllers/confirmations_controller.rb4
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb4
-rw-r--r--app/controllers/passwords_controller.rb4
-rw-r--r--app/controllers/registrations_controller.rb4
-rw-r--r--app/graphql/types/ci/runner_type_enum.rb8
-rw-r--r--app/graphql/types/member_interface.rb2
-rw-r--r--app/models/concerns/cache_markdown_field.rb6
-rw-r--r--app/models/integrations/builds_email.rb16
-rw-r--r--app/models/member.rb8
-rw-r--r--app/services/issues/zoom_link_service.rb4
-rw-r--r--app/services/members/create_service.rb2
-rw-r--r--app/views/projects/_invite_members.html.haml10
-rw-r--r--db/migrate/20210603222333_remove_builds_email_service_from_services.rb11
-rw-r--r--db/schema_migrations/202106032223331
-rw-r--r--doc/api/graphql/reference/index.md6
-rw-r--r--doc/api/groups.md62
-rw-r--r--doc/api/runners.md16
-rw-r--r--doc/ci/examples/authenticating-with-hashicorp-vault/index.md4
-rw-r--r--doc/ci/yaml/README.md7
-rw-r--r--doc/topics/autodevops/customize.md3
-rw-r--r--doc/user/project/integrations/webhooks.md6
-rw-r--r--doc/user/project/merge_requests/browser_performance_testing.md23
-rw-r--r--lib/api/entities/runner.rb1
-rw-r--r--lib/api/members.rb2
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Ruby.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/yaml_processor/result.rb8
-rw-r--r--lib/gitlab/data_builder/build.rb2
-rw-r--r--lib/gitlab/data_builder/pipeline.rb2
-rw-r--r--lib/gitlab/markdown_cache/field_data.rb2
-rw-r--r--lib/tasks/gitlab/db.rake4
-rw-r--r--locale/gitlab.pot36
-rw-r--r--qa/qa/specs/features/browser_ui/8_monitor/cluster_with_prometheus.rb2
-rwxr-xr-xscripts/frontend/startup_css/startup_css_changed.sh2
-rw-r--r--spec/controllers/confirmations_controller_spec.rb64
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb24
-rw-r--r--spec/controllers/passwords_controller_spec.rb12
-rw-r--r--spec/controllers/registrations_controller_spec.rb24
-rw-r--r--spec/features/groups/members/manage_members_spec.rb57
-rw-r--r--spec/features/projects/members/list_spec.rb34
-rw-r--r--spec/features/projects/user_views_empty_project_spec.rb23
-rw-r--r--spec/frontend/fixtures/runner.rb71
-rw-r--r--spec/frontend/invite_members/components/invite_members_modal_spec.js50
-rw-r--r--spec/frontend/repository/components/table/row_spec.js21
-rw-r--r--spec/frontend/runner/components/runner_list_spec.js4
-rw-r--r--spec/frontend/runner/mock_data.js50
-rw-r--r--spec/frontend/runner/runner_detail/runner_details_app_spec.js21
-rw-r--r--spec/frontend/runner/runner_list/runner_list_app_spec.js16
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/result_spec.rb24
-rw-r--r--spec/lib/gitlab/data_builder/build_spec.rb2
-rw-r--r--spec/lib/gitlab/data_builder/pipeline_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown_cache/field_data_spec.rb7
-rw-r--r--spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb24
-rw-r--r--spec/models/member_spec.rb14
-rw-r--r--spec/requests/api/ci/runners_spec.rb10
-rw-r--r--spec/requests/api/graphql/group/group_members_spec.rb17
-rw-r--r--spec/requests/api/graphql/project/project_members_spec.rb14
-rw-r--r--spec/requests/api/invitations_spec.rb6
-rw-r--r--spec/requests/api/members_spec.rb12
-rw-r--r--spec/services/issues/zoom_link_service_spec.rb10
-rw-r--r--spec/services/members/create_service_spec.rb9
-rw-r--r--spec/services/security/ci_configuration/sast_parser_service_spec.rb17
-rw-r--r--spec/tasks/gitlab/db_rake_spec.rb16
-rw-r--r--spec/views/projects/empty.html.haml_spec.rb12
73 files changed, 694 insertions, 310 deletions
diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue
index d00a0f1633b..6628fc4063b 100644
--- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue
+++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue
@@ -68,6 +68,7 @@ export default {
newUsersToInvite: [],
selectedDate: undefined,
groupToBeSharedWith: {},
+ source: 'unknown',
};
},
computed: {
@@ -195,6 +196,7 @@ export default {
...this.basePostData,
email: usersToInviteByEmail,
access_level: this.selectedAccessLevel,
+ invite_source: this.source,
};
},
addByUserIdPostData(usersToAddById) {
@@ -202,6 +204,7 @@ export default {
...this.basePostData,
user_id: usersToAddById,
access_level: this.selectedAccessLevel,
+ invite_source: this.source,
};
},
shareWithGroupPostData(groupToBeSharedWith) {
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index 83e43d7ac48..26f8018a968 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -3,6 +3,8 @@ import Activities from '~/activities';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import BlobViewer from '~/blob/viewer/index';
import { initUploadForm } from '~/blob_edit/blob_bundle';
+import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
+import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
import leaveByUrl from '~/namespaces/leave_by_url';
import initVueNotificationsDropdown from '~/notifications';
import { initUploadFileTrigger } from '~/projects/upload_file_experiment';
@@ -44,3 +46,5 @@ initVueNotificationsDropdown();
new ShortcutsNavigation(); // eslint-disable-line no-new
initUploadFileTrigger();
+initInviteMembersModal();
+initInviteMembersTrigger();
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index 8ea5fce92fa..1b7fb6a63df 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -7,13 +7,17 @@ import {
GlTooltipDirective,
GlLoadingIcon,
GlIcon,
+ GlHoverLoadDirective,
} from '@gitlab/ui';
import { escapeRegExp } from 'lodash';
+import filesQuery from 'shared_queries/repository/files.query.graphql';
import { escapeFileUrl } from '~/lib/utils/url_utility';
+import { TREE_PAGE_SIZE } from '~/repository/constants';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import getRefMixin from '../../mixins/get_ref';
+import blobInfoQuery from '../../queries/blob_info.query.graphql';
import commitQuery from '../../queries/commit.query.graphql';
export default {
@@ -28,6 +32,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
+ GlHoverLoad: GlHoverLoadDirective,
},
apollo: {
commit: {
@@ -139,6 +144,33 @@ export default {
return this.commit && this.commit.lockLabel;
},
},
+ methods: {
+ handlePreload() {
+ return this.isFolder ? this.loadFolder() : this.loadBlob();
+ },
+ loadFolder() {
+ this.apolloQuery(filesQuery, {
+ projectPath: this.projectPath,
+ ref: this.ref,
+ path: this.path,
+ nextPageCursor: '',
+ pageSize: TREE_PAGE_SIZE,
+ });
+ },
+ loadBlob() {
+ if (!this.refactorBlobViewerEnabled) {
+ return;
+ }
+
+ this.apolloQuery(blobInfoQuery, {
+ projectPath: this.projectPath,
+ filePath: this.path,
+ });
+ },
+ apolloQuery(query, variables) {
+ this.$apollo.query({ query, variables });
+ },
+ },
};
</script>
@@ -148,6 +180,7 @@ export default {
<component
:is="linkComponent"
ref="link"
+ v-gl-hover-load="handlePreload"
:to="routerLinkTo"
:href="url"
:class="{
diff --git a/app/assets/javascripts/repository/queries/blob_info.query.graphql b/app/assets/javascripts/repository/queries/blob_info.query.graphql
index 9eee8b2eef3..59d60c9f174 100644
--- a/app/assets/javascripts/repository/queries/blob_info.query.graphql
+++ b/app/assets/javascripts/repository/queries/blob_info.query.graphql
@@ -1,6 +1,5 @@
query getBlobInfo($projectPath: ID!, $filePath: String!) {
project(fullPath: $projectPath) {
- id
repository {
blobs(paths: [$filePath]) {
nodes {
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 00b9fb1060d..a08f1f5d391 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -457,9 +457,7 @@ class ApplicationController < ActionController::Base
def set_current_context(&block)
Gitlab::ApplicationContext.with_context(
- # Avoid loading the auth_user again after the request. Otherwise calling
- # `auth_user` again would also trigger the Warden callbacks again
- user: -> { auth_user if strong_memoized?(:auth_user) },
+ user: -> { context_user },
project: -> { @project if @project&.persisted? },
namespace: -> { @group if @group&.persisted? },
caller_id: caller_id,
@@ -542,6 +540,12 @@ class ApplicationController < ActionController::Base
end
end
+ # Avoid loading the auth_user again after the request. Otherwise calling
+ # `auth_user` again would also trigger the Warden callbacks again
+ def context_user
+ auth_user if strong_memoized?(:auth_user)
+ end
+
def caller_id
"#{self.class.name}##{action_name}"
end
diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb
index e82500912fa..0b833e149a4 100644
--- a/app/controllers/confirmations_controller.rb
+++ b/app/controllers/confirmations_controller.rb
@@ -34,6 +34,10 @@ class ConfirmationsController < Devise::ConfirmationsController
def after_sign_in(resource)
after_sign_in_path_for(resource)
end
+
+ def context_user
+ resource
+ end
end
ConfirmationsController.prepend_mod_with('ConfirmationsController')
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 31f404a9974..9d7a1712698 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -287,6 +287,10 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def fail_admin_mode_invalid_credentials
redirect_to new_admin_session_path, alert: _('Invalid login or password')
end
+
+ def context_user
+ current_user
+ end
end
OmniauthCallbacksController.prepend_mod_with('OmniauthCallbacksController')
diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb
index 2c0ed825daa..c764f2d0459 100644
--- a/app/controllers/passwords_controller.rb
+++ b/app/controllers/passwords_controller.rb
@@ -67,6 +67,10 @@ class PasswordsController < Devise::PasswordsController
redirect_to new_user_session_path,
notice: I18n.t('devise.passwords.send_paranoid_instructions')
end
+
+ def context_user
+ resource
+ end
end
PasswordsController.prepend_mod_with('PasswordsController')
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 0f29f6f608f..a0710c1f8e7 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -202,6 +202,10 @@ class RegistrationsController < Devise::RegistrationsController
experiment(:invite_signup_page_interaction, actor: member).track(:form_submission)
experiment('members/invite_email', actor: member).track(:accepted)
end
+
+ def context_user
+ current_user
+ end
end
RegistrationsController.prepend_mod_with('RegistrationsController')
diff --git a/app/graphql/types/ci/runner_type_enum.rb b/app/graphql/types/ci/runner_type_enum.rb
index f771635f4ed..12e87906179 100644
--- a/app/graphql/types/ci/runner_type_enum.rb
+++ b/app/graphql/types/ci/runner_type_enum.rb
@@ -5,10 +5,10 @@ module Types
class RunnerTypeEnum < BaseEnum
graphql_name 'CiRunnerType'
- ::Ci::Runner.runner_types.keys.each do |type|
- value type.upcase,
- description: "A runner that is #{type.tr('_', ' ')}.",
- value: type
+ ::Ci::Runner::AVAILABLE_TYPES.each do |runner_type|
+ value runner_type.upcase,
+ description: "A runner that is #{runner_type.tr('_', ' ')}.",
+ value: runner_type
end
end
end
diff --git a/app/graphql/types/member_interface.rb b/app/graphql/types/member_interface.rb
index 1c7257487d9..6a21e51fe28 100644
--- a/app/graphql/types/member_interface.rb
+++ b/app/graphql/types/member_interface.rb
@@ -22,7 +22,7 @@ module Types
field :expires_at, Types::TimeType, null: true,
description: 'Date and time the membership expires.'
- field :user, Types::UserType, null: false,
+ field :user, Types::UserType, null: true,
description: 'User that is associated with the member object.'
definition_methods do
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index a5cf947ba07..87a1bb877ae 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -27,7 +27,7 @@ module CacheMarkdownField
# Returns the default Banzai render context for the cached markdown field.
def banzai_render_context(field)
raise ArgumentError, "Unknown field: #{field.inspect}" unless
- cached_markdown_fields.markdown_fields.include?(field)
+ cached_markdown_fields.key?(field)
# Always include a project key, or Banzai complains
project = self.project if self.respond_to?(:project)
@@ -100,7 +100,7 @@ module CacheMarkdownField
def cached_html_for(markdown_field)
raise ArgumentError, "Unknown field: #{markdown_field}" unless
- cached_markdown_fields.markdown_fields.include?(markdown_field)
+ cached_markdown_fields.key?(markdown_field)
__send__(cached_markdown_fields.html_field(markdown_field)) # rubocop:disable GitlabSecurity/PublicSend
end
@@ -108,7 +108,7 @@ module CacheMarkdownField
# Updates the markdown cache if necessary, then returns the field
# Unlike `cached_html_for` it returns `nil` if the field does not exist
def updated_cached_html_for(markdown_field)
- return unless cached_markdown_fields.markdown_fields.include?(markdown_field)
+ return unless cached_markdown_fields.key?(markdown_field)
if attribute_invalidated?(cached_markdown_fields.html_field(markdown_field))
# Invalidated due to Markdown content change
diff --git a/app/models/integrations/builds_email.rb b/app/models/integrations/builds_email.rb
deleted file mode 100644
index 2628848667e..00000000000
--- a/app/models/integrations/builds_email.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-# This class is to be removed with 9.1
-# We should also by then remove BuildsEmailService from database
-# https://gitlab.com/gitlab-org/gitlab/-/issues/331064
-module Integrations
- class BuildsEmail < Integration
- def self.to_param
- 'builds_email'
- end
-
- def self.supported_events
- %w[]
- end
- end
-end
diff --git a/app/models/member.rb b/app/models/member.rb
index b8203849ee9..0636c3c2d4e 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -108,10 +108,14 @@ class Member < ApplicationRecord
scope :active_without_invites_and_requests, -> do
left_join_users
.where(users: { state: 'active' })
- .non_request
+ .without_invites_and_requests
+ .reorder(nil)
+ end
+
+ scope :without_invites_and_requests, -> do
+ non_request
.non_invite
.non_minimal_access
- .reorder(nil)
end
scope :invite, -> { where.not(invite_token: nil) }
diff --git a/app/services/issues/zoom_link_service.rb b/app/services/issues/zoom_link_service.rb
index ef48134dec4..1ce459aa7e6 100644
--- a/app/services/issues/zoom_link_service.rb
+++ b/app/services/issues/zoom_link_service.rb
@@ -47,11 +47,11 @@ module Issues
attr_reader :issue
def track_meeting_added_event
- ::Gitlab::Tracking.event('IncidentManagement::ZoomIntegration', 'add_zoom_meeting', label: 'Issue ID', value: issue.id)
+ ::Gitlab::Tracking.event('IncidentManagement::ZoomIntegration', 'add_zoom_meeting', label: 'Issue ID', value: issue.id, user: current_user, project: @project, namespace: @project.namespace)
end
def track_meeting_removed_event
- ::Gitlab::Tracking.event('IncidentManagement::ZoomIntegration', 'remove_zoom_meeting', label: 'Issue ID', value: issue.id)
+ ::Gitlab::Tracking.event('IncidentManagement::ZoomIntegration', 'remove_zoom_meeting', label: 'Issue ID', value: issue.id, user: current_user, project: @project, namespace: @project.namespace)
end
def add_zoom_meeting(link)
diff --git a/app/services/members/create_service.rb b/app/services/members/create_service.rb
index d3a0b6ca62d..f2556966708 100644
--- a/app/services/members/create_service.rb
+++ b/app/services/members/create_service.rb
@@ -75,7 +75,7 @@ module Members
def after_execute(member:)
super
- Gitlab::Tracking.event(self.class.name, 'create_member', label: invite_source, property: tracking_property(member))
+ Gitlab::Tracking.event(self.class.name, 'create_member', label: invite_source, property: tracking_property(member), user: current_user)
end
def invite_source
diff --git a/app/views/projects/_invite_members.html.haml b/app/views/projects/_invite_members.html.haml
index e3a512d6451..ab630d34501 100644
--- a/app/views/projects/_invite_members.html.haml
+++ b/app/views/projects/_invite_members.html.haml
@@ -3,6 +3,10 @@
track_event: 'render' } }
= s_('InviteMember|Invite your team')
%p= s_('InviteMember|Add members to this project and start collaborating with your team.')
-= link_to s_('InviteMember|Invite members'), project_project_members_path(@project, sort: :access_level_desc),
- class: 'gl-button btn btn-confirm gl-mb-8 gl-xs-w-full',
- data: { track_event: 'click_button', track_label: 'invite_members_empty_project' }
+.js-invite-members-trigger{ data: { variant: 'confirm',
+ classes: 'gl-mb-8 gl-xs-w-full',
+ display_text: s_('InviteMember|Invite members'),
+ event: 'click_button',
+ label: 'invite_members_empty_project' } }
+
+= render 'shared/issuable/invite_members_trigger', project: @project
diff --git a/db/migrate/20210603222333_remove_builds_email_service_from_services.rb b/db/migrate/20210603222333_remove_builds_email_service_from_services.rb
new file mode 100644
index 00000000000..791b8b659af
--- /dev/null
+++ b/db/migrate/20210603222333_remove_builds_email_service_from_services.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class RemoveBuildsEmailServiceFromServices < ActiveRecord::Migration[6.1]
+ def up
+ execute("DELETE from services WHERE type = 'BuildsEmailService'")
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20210603222333 b/db/schema_migrations/20210603222333
new file mode 100644
index 00000000000..25b5055f17e
--- /dev/null
+++ b/db/schema_migrations/20210603222333
@@ -0,0 +1 @@
+fb02e0fee2760dad203b54d81c342dbf1461b3010503cab05da1eb14ab5d33da \ No newline at end of file
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index b54a36b6024..3a6433722f5 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -9501,7 +9501,7 @@ Represents a Group Membership.
| <a id="groupmembergroup"></a>`group` | [`Group`](#group) | Group that a User is a member of. |
| <a id="groupmemberid"></a>`id` | [`ID!`](#id) | ID of the member. |
| <a id="groupmemberupdatedat"></a>`updatedAt` | [`Time`](#time) | Date and time the membership was last updated. |
-| <a id="groupmemberuser"></a>`user` | [`UserCore!`](#usercore) | User that is associated with the member object. |
+| <a id="groupmemberuser"></a>`user` | [`UserCore`](#usercore) | User that is associated with the member object. |
| <a id="groupmemberuserpermissions"></a>`userPermissions` | [`GroupPermissions!`](#grouppermissions) | Permissions for the current user on the resource. |
### `GroupPermissions`
@@ -11916,7 +11916,7 @@ Represents a Project Membership.
| <a id="projectmemberid"></a>`id` | [`ID!`](#id) | ID of the member. |
| <a id="projectmemberproject"></a>`project` | [`Project`](#project) | Project that User is a member of. |
| <a id="projectmemberupdatedat"></a>`updatedAt` | [`Time`](#time) | Date and time the membership was last updated. |
-| <a id="projectmemberuser"></a>`user` | [`UserCore!`](#usercore) | User that is associated with the member object. |
+| <a id="projectmemberuser"></a>`user` | [`UserCore`](#usercore) | User that is associated with the member object. |
| <a id="projectmemberuserpermissions"></a>`userPermissions` | [`ProjectPermissions!`](#projectpermissions) | Permissions for the current user on the resource. |
### `ProjectPermissions`
@@ -15687,7 +15687,7 @@ Implementations:
| <a id="memberinterfaceexpiresat"></a>`expiresAt` | [`Time`](#time) | Date and time the membership expires. |
| <a id="memberinterfaceid"></a>`id` | [`ID!`](#id) | ID of the member. |
| <a id="memberinterfaceupdatedat"></a>`updatedAt` | [`Time`](#time) | Date and time the membership was last updated. |
-| <a id="memberinterfaceuser"></a>`user` | [`UserCore!`](#usercore) | User that is associated with the member object. |
+| <a id="memberinterfaceuser"></a>`user` | [`UserCore`](#usercore) | User that is associated with the member object. |
#### `Noteable`
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 6ea2e5c5026..5976f0f005d 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -752,7 +752,7 @@ Parameters:
| `name` | string | yes | The name of the group. |
| `path` | string | yes | The path of the group. |
| `description` | string | no | The group's description. |
-| `membership_lock` | boolean | no | **(STARTER)** Prevent adding new members to project membership within this group. |
+| `membership_lock` | boolean | no | **(PREMIUM)** Prevent adding new members to project membership within this group. |
| `visibility` | string | no | The group's visibility. Can be `private`, `internal`, or `public`. |
| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
| `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. |
@@ -828,7 +828,7 @@ PUT /groups/:id
| `name` | string | no | The name of the group. |
| `path` | string | no | The path of the group. |
| `description` | string | no | The description of the group. |
-| `membership_lock` | boolean | no | **(STARTER)** Prevent adding new members to project membership within this group. |
+| `membership_lock` | boolean | no | **(PREMIUM)** Prevent adding new members to project membership within this group. |
| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
| `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. |
@@ -1146,7 +1146,7 @@ DELETE /groups/:id/hooks/:hook_id
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
| `hook_id` | integer | yes | The ID of the group hook. |
-## Group Audit Events **(STARTER)**
+## Group Audit Events **(PREMIUM)**
Group audit events can be accessed via the [Group Audit Events API](audit_events.md#group-audit-events)
@@ -1302,11 +1302,11 @@ DELETE /groups/:id/share/:group_id
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
| `group_id` | integer | yes | The ID of the group to share with |
-## Push Rules **(STARTER)**
+## Push Rules **(PREMIUM)**
-> Introduced in [GitLab Starter](https://about.gitlab.com/pricing/) 13.4.
+> Introduced in [GitLab](https://about.gitlab.com/pricing/) 13.4.
-### Get group push rules **(STARTER)**
+### Get group push rules **(PREMIUM)**
Get the [push rules](../user/group/index.md#group-push-rules) of a group.
@@ -1349,7 +1349,7 @@ the `commit_committer_check` and `reject_unsigned_commits` parameters:
}
```
-### Add group push rule **(STARTER)**
+### Add group push rule **(PREMIUM)**
Adds [push rules](../user/group/index.md#group-push-rules) to the specified group.
@@ -1362,17 +1362,17 @@ POST /groups/:id/push_rule
| Attribute | Type | Required | Description |
| --------------------------------------------- | -------------- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
-| `deny_delete_tag` **(STARTER)** | boolean | no | Deny deleting a tag |
-| `member_check` **(STARTER)** | boolean | no | Allows only GitLab users to author commits |
-| `prevent_secrets` **(STARTER)** | boolean | no | [Files that are likely to contain secrets](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml) are rejected |
-| `commit_message_regex` **(STARTER)** | string | no | All commit messages must match the regular expression provided in this attribute, e.g. `Fixed \d+\..*` |
-| `commit_message_negative_regex` **(STARTER)** | string | no | Commit messages matching the regular expression provided in this attribute aren't allowed, e.g. `ssh\:\/\/` |
-| `branch_name_regex` **(STARTER)** | string | no | All branch names must match the regular expression provided in this attribute, e.g. `(feature|hotfix)\/*` |
-| `author_email_regex` **(STARTER)** | string | no | All commit author emails must match the regular expression provided in this attribute, e.g. `@my-company.com$` |
-| `file_name_regex` **(STARTER)** | string | no | Filenames matching the regular expression provided in this attribute are **not** allowed, e.g. `(jar|exe)$` |
-| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) allowed |
-| `commit_committer_check` **(PREMIUM)** | boolean | no | Only commits pushed using verified emails are allowed |
-| `reject_unsigned_commits` **(PREMIUM)** | boolean | no | Only commits signed through GPG are allowed |
+| `deny_delete_tag` | boolean | no | Deny deleting a tag |
+| `member_check` | boolean | no | Allows only GitLab users to author commits |
+| `prevent_secrets` | boolean | no | [Files that are likely to contain secrets](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml) are rejected |
+| `commit_message_regex` | string | no | All commit messages must match the regular expression provided in this attribute, e.g. `Fixed \d+\..*` |
+| `commit_message_negative_regex` | string | no | Commit messages matching the regular expression provided in this attribute aren't allowed, e.g. `ssh\:\/\/` |
+| `branch_name_regex` | string | no | All branch names must match the regular expression provided in this attribute, e.g. `(feature|hotfix)\/*` |
+| `author_email_regex` | string | no | All commit author emails must match the regular expression provided in this attribute, e.g. `@my-company.com$` |
+| `file_name_regex` | string | no | Filenames matching the regular expression provided in this attribute are **not** allowed, e.g. `(jar|exe)$` |
+| `max_file_size` | integer | no | Maximum file size (MB) allowed |
+| `commit_committer_check` | boolean | no | Only commits pushed using verified emails are allowed |
+| `reject_unsigned_commits` | boolean | no | Only commits signed through GPG are allowed |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/19/push_rule"
@@ -1396,7 +1396,7 @@ Response:
}
```
-### Edit group push rule **(STARTER)**
+### Edit group push rule **(PREMIUM)**
Edit push rules for a specified group.
@@ -1409,17 +1409,17 @@ PUT /groups/:id/push_rule
| Attribute | Type | Required | Description |
| --------------------------------------------- | -------------- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
-| `deny_delete_tag` **(STARTER)** | boolean | no | Deny deleting a tag |
-| `member_check` **(STARTER)** | boolean | no | Restricts commits to be authored by existing GitLab users only |
-| `prevent_secrets` **(STARTER)** | boolean | no | [Files that are likely to contain secrets](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml) are rejected |
-| `commit_message_regex` **(STARTER)** | string | no | All commit messages must match the regular expression provided in this attribute, e.g. `Fixed \d+\..*` |
-| `commit_message_negative_regex` **(STARTER)** | string | no | Commit messages matching the regular expression provided in this attribute aren't allowed, e.g. `ssh\:\/\/` |
-| `branch_name_regex` **(STARTER)** | string | no | All branch names must match the regular expression provided in this attribute, e.g. `(feature|hotfix)\/*` |
-| `author_email_regex` **(STARTER)** | string | no | All commit author emails must match the regular expression provided in this attribute, e.g. `@my-company.com$` |
-| `file_name_regex` **(STARTER)** | string | no | Filenames matching the regular expression provided in this attribute are **not** allowed, e.g. `(jar|exe)$` |
-| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) allowed |
-| `commit_committer_check` **(PREMIUM)** | boolean | no | Only commits pushed using verified emails are allowed |
-| `reject_unsigned_commits` **(PREMIUM)** | boolean | no | Only commits signed through GPG are allowed |
+| `deny_delete_tag` | boolean | no | Deny deleting a tag |
+| `member_check` | boolean | no | Restricts commits to be authored by existing GitLab users only |
+| `prevent_secrets` | boolean | no | [Files that are likely to contain secrets](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml) are rejected |
+| `commit_message_regex` | string | no | All commit messages must match the regular expression provided in this attribute, e.g. `Fixed \d+\..*` |
+| `commit_message_negative_regex` | string | no | Commit messages matching the regular expression provided in this attribute aren't allowed, e.g. `ssh\:\/\/` |
+| `branch_name_regex` | string | no | All branch names must match the regular expression provided in this attribute, e.g. `(feature|hotfix)\/*` |
+| `author_email_regex` | string | no | All commit author emails must match the regular expression provided in this attribute, e.g. `@my-company.com$` |
+| `file_name_regex` | string | no | Filenames matching the regular expression provided in this attribute are **not** allowed, e.g. `(jar|exe)$` |
+| `max_file_size` | integer | no | Maximum file size (MB) allowed |
+| `commit_committer_check` | boolean | no | Only commits pushed using verified emails are allowed |
+| `reject_unsigned_commits` | boolean | no | Only commits signed through GPG are allowed |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/19/push_rule"
@@ -1443,7 +1443,7 @@ Response:
}
```
-### Delete group push rule **(STARTER)**
+### Delete group push rule **(PREMIUM)**
Deletes the [push rules](../user/group/index.md#group-push-rules) of a group.
diff --git a/doc/api/runners.md b/doc/api/runners.md
index d8f600670ff..a6886b75a1c 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -62,8 +62,9 @@ Example response:
"active": true,
"description": "test-1-20150125",
"id": 6,
- "is_shared": false,
"ip_address": "127.0.0.1",
+ "is_shared": false,
+ "runner_type": "project_type",
"name": null,
"online": true,
"status": "online"
@@ -74,6 +75,7 @@ Example response:
"id": 8,
"ip_address": "127.0.0.1",
"is_shared": false,
+ "runner_type": "group_type",
"name": null,
"online": false,
"status": "offline"
@@ -115,6 +117,7 @@ Example response:
"id": 1,
"ip_address": "127.0.0.1",
"is_shared": true,
+ "runner_type": "instance_type",
"name": null,
"online": true,
"status": "online"
@@ -125,6 +128,7 @@ Example response:
"id": 3,
"ip_address": "127.0.0.1",
"is_shared": true,
+ "runner_type": "instance_type",
"name": null,
"online": false,
"status": "offline"
@@ -135,6 +139,7 @@ Example response:
"id": 6,
"ip_address": "127.0.0.1",
"is_shared": false,
+ "runner_type": "project_type",
"name": null,
"online": true,
"status": "paused"
@@ -145,6 +150,7 @@ Example response:
"id": 8,
"ip_address": "127.0.0.1",
"is_shared": false,
+ "runner_type": "group_type",
"name": null,
"online": false,
"status": "offline"
@@ -187,6 +193,7 @@ Example response:
"id": 6,
"ip_address": "127.0.0.1",
"is_shared": false,
+ "runner_type": "project_type",
"contacted_at": "2016-01-25T16:39:48.066Z",
"name": null,
"online": true,
@@ -250,6 +257,7 @@ Example response:
"id": 6,
"ip_address": "127.0.0.1",
"is_shared": false,
+ "runner_type": "group_type",
"contacted_at": "2016-01-25T16:39:48.066Z",
"name": null,
"online": true,
@@ -420,6 +428,7 @@ Example response:
"id": 8,
"ip_address": "127.0.0.1",
"is_shared": false,
+ "runner_type": "project_type",
"name": null,
"online": false,
"status": "offline"
@@ -430,6 +439,7 @@ Example response:
"id": 5,
"ip_address": "127.0.0.1",
"is_shared": true,
+ "runner_type": "instance_type",
"name": null,
"online": true,
"status": "paused"
@@ -464,6 +474,7 @@ Example response:
"id": 9,
"ip_address": "127.0.0.1",
"is_shared": false,
+ "runner_type": "project_type",
"name": null,
"online": true,
"status": "online"
@@ -522,6 +533,7 @@ Example response:
"ip_address": "127.0.0.1",
"active": true,
"is_shared": true,
+ "runner_type": "instance_type",
"name": "gitlab-runner",
"online": null,
"status": "not_connected"
@@ -532,6 +544,7 @@ Example response:
"ip_address": "127.0.0.1",
"active": true,
"is_shared": true,
+ "runner_type": "instance_type",
"name": "gitlab-runner",
"online": false,
"status": "offline"
@@ -542,6 +555,7 @@ Example response:
"ip_address": "127.0.0.1",
"active": true,
"is_shared": false,
+ "runner_type": "group_type",
"name": "gitlab-runner",
"online": null,
"status": "not_connected"
diff --git a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
index 703b8dde94d..fc1e06e91c6 100644
--- a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
+++ b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
@@ -203,6 +203,10 @@ read_secrets:
- export PASSWORD="$(vault kv get -field=password secret/myproject/production/db)"
```
+NOTE:
+If you're using a Vault instance provided by HashiCorp Cloud Platform,
+you need to export the `VAULT_NAMESPACE` variable. Its default value is `admin`.
+
![read_secrets staging](img/vault-read-secrets-staging.png)
The following job is able to authenticate using the `myproject-production` role and read secrets under `/secret/myproject/production/`:
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index d8357d455a8..834bb442b6b 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -3306,7 +3306,7 @@ job:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49775) in GitLab 13.8
> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
-> - It's enabled on GitLab.com.
+> - It's disabled on GitLab.com.
> - It's recommended for production use.
Use `artifacts:public` to determine whether the job artifacts should be
@@ -3524,12 +3524,13 @@ as artifacts.
The collected Metrics report uploads to GitLab as an artifact and displays in merge requests.
-##### `artifacts:reports:performance` **(PREMIUM)**
+##### `artifacts:reports:browser_performance` **(PREMIUM)**
> - Introduced in GitLab 11.5.
> - Requires GitLab Runner 11.5 and above.
+> - [Name changed](https://gitlab.com/gitlab-org/gitlab/-/issues/225914) from `artifacts:reports:performance` in GitLab 14.0.
-The `performance` report collects [Browser Performance Testing metrics](../../user/project/merge_requests/browser_performance_testing.md)
+The `browser_performance` report collects [Browser Performance Testing metrics](../../user/project/merge_requests/browser_performance_testing.md)
as artifacts.
The collected Browser Performance report uploads to GitLab as an artifact and displays in merge requests.
diff --git a/doc/topics/autodevops/customize.md b/doc/topics/autodevops/customize.md
index b9509039cb0..75088c9a4e6 100644
--- a/doc/topics/autodevops/customize.md
+++ b/doc/topics/autodevops/customize.md
@@ -431,7 +431,8 @@ The following table lists variables used to disable jobs.
| `license_scanning` | `LICENSE_MANAGEMENT_DISABLED` | [From GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job isn't created. |
| `load_performance` | `LOAD_PERFORMANCE_DISABLED` | From GitLab 13.2 | If the variable is present, the job isn't created. |
| `nodejs-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
-| `performance` | `PERFORMANCE_DISABLED` | From GitLab 11.0 | Browser performance. If the variable is present, the job isn't created. |
+| `performance` | `PERFORMANCE_DISABLED` | GitLab 11.0 to GitLab 13.12 | Browser performance. If the variable is present, the job isn't created. Replaced by `browser_peformance`. |
+| `browser_performance` | `BROWSER_PERFORMANCE_DISABLED` | From GitLab 14.0 | Browser performance. If the variable is present, the job isn't created. Replaces `performance`. |
| `phpcs-security-audit-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
| `pmd-apex-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
| `retire-js-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index d74a2bec1f6..e049c19267b 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -1161,6 +1161,7 @@ X-Gitlab-Event: Pipeline Hook
"id": 380987,
"description": "shared-runners-manager-6.gitlab.com",
"active": true,
+ "runner_type": "instance_type",
"is_shared": true,
"tags": [
"linux",
@@ -1196,7 +1197,8 @@ X-Gitlab-Event: Pipeline Hook
"id":380987,
"description":"shared-runners-manager-6.gitlab.com",
"active":true,
- "is_shared":true,
+ "runner_type": "instance_type",
+ "is_shared": true,
"tags": [
"linux",
"docker"
@@ -1230,6 +1232,7 @@ X-Gitlab-Event: Pipeline Hook
"id": 380987,
"description": "shared-runners-manager-6.gitlab.com",
"active": true,
+ "runner_type": "instance_type",
"is_shared": true,
"tags": [
"linux",
@@ -1333,6 +1336,7 @@ X-Gitlab-Event: Job Hook
},
"runner": {
"active": true,
+ "runner_type": "project_type",
"is_shared": false,
"id": 380987,
"description": "shared-runners-manager-6.gitlab.com",
diff --git a/doc/user/project/merge_requests/browser_performance_testing.md b/doc/user/project/merge_requests/browser_performance_testing.md
index bafe6795d52..58c737f375f 100644
--- a/doc/user/project/merge_requests/browser_performance_testing.md
+++ b/doc/user/project/merge_requests/browser_performance_testing.md
@@ -40,11 +40,11 @@ Consider the following workflow:
## How browser performance testing works
First, define a job in your `.gitlab-ci.yml` file that generates the
-[Browser Performance report artifact](../../../ci/yaml/README.md#artifactsreportsperformance).
+[Browser Performance report artifact](../../../ci/yaml/README.md#artifactsreportsbrowser_performance).
GitLab then checks this report, compares key performance metrics for each page
between the source and target branches, and shows the information in the merge request.
-For an example Performance job, see
+For an example Browser Performance job, see
[Configuring Browser Performance Testing](#configuring-browser-performance-testing).
NOTE:
@@ -70,18 +70,17 @@ using Docker-in-Docker.
include:
template: Verify/Browser-Performance.gitlab-ci.yml
- performance:
+ browser_performance:
variables:
URL: https://example.com
```
WARNING:
-In GitLab 14.0 and later, the job [is scheduled to be renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/225914)
-from `performance` to `browser_performance`.
+In GitLab 13.12 and earlier, the job [was named](https://gitlab.com/gitlab-org/gitlab/-/issues/225914) `performance`.
The above example:
-- Creates a `performance` job in your CI/CD pipeline and runs sitespeed.io against the webpage you
+- Creates a `browser_performance` job in your CI/CD pipeline and runs sitespeed.io against the webpage you
defined in `URL` to gather key metrics.
- Uses a template that doesn't work with Kubernetes clusters. If you are using a Kubernetes cluster,
use [`template: Jobs/Browser-Performance-Testing.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml)
@@ -90,7 +89,7 @@ The above example:
GitLab 12.3 or earlier, you must [add the configuration manually](#gitlab-versions-132-and-earlier).
The template uses the [GitLab plugin for sitespeed.io](https://gitlab.com/gitlab-org/gl-performance),
-and it saves the full HTML sitespeed.io report as a [Browser Performance report artifact](../../../ci/yaml/README.md#artifactsreportsperformance)
+and it saves the full HTML sitespeed.io report as a [Browser Performance report artifact](../../../ci/yaml/README.md#artifactsreportsbrowser_performance)
that you can later download and analyze. This implementation always takes the latest
Browser Performance artifact available. If [GitLab Pages](../pages/index.md) is enabled,
you can view the report directly in your browser.
@@ -108,7 +107,7 @@ makes on the given URL, and change the version:
include:
template: Verify/Browser-Performance.gitlab-ci.yml
-performance:
+browser_performance:
variables:
URL: https://www.sitespeed.io/
SITESPEED_VERSION: 13.2.0
@@ -127,7 +126,7 @@ if the `Total Score` metric degrades by 5 points or more:
include:
template: Verify/Browser-Performance.gitlab-ci.yml
-performance:
+browser_performance:
variables:
URL: https://example.com
DEGRADATION_THRESHOLD: 5
@@ -140,13 +139,13 @@ The `Total Score` metric is based on sitespeed.io's [coach performance score](ht
The above CI YAML configuration is great for testing against static environments, and it can
be extended for dynamic environments, but a few extra steps are required:
-1. The `performance` job should run after the dynamic environment has started.
+1. The `browser_performance` job should run after the dynamic environment has started.
1. In the `review` job:
1. Generate a URL list file with the dynamic URL.
1. Save the file as an artifact, for example with `echo $CI_ENVIRONMENT_URL > environment_url.txt`
in your job's `script`.
1. Pass the list as the URL environment variable (which can be a URL or a file containing URLs)
- to the `performance` job.
+ to the `browser_performance` job.
1. You can now run the sitespeed.io container against the desired hostname and
paths.
@@ -176,7 +175,7 @@ review:
except:
- master
-performance:
+browser_performance:
dependencies:
- review
variables:
diff --git a/lib/api/entities/runner.rb b/lib/api/entities/runner.rb
index 6165b54cddb..e78f14cf920 100644
--- a/lib/api/entities/runner.rb
+++ b/lib/api/entities/runner.rb
@@ -8,6 +8,7 @@ module API
expose :ip_address
expose :active
expose :instance_type?, as: :is_shared
+ expose :runner_type
expose :name
expose :online?, as: :online
expose :status
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 49241c8d6b2..0575eea1eab 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -117,7 +117,7 @@ module API
not_allowed! # This currently can only be reached in EE
elsif member.valid? && member.persisted?
present_members(member)
- Gitlab::Tracking.event(::Members::CreateService.name, 'create_member', label: params[:invite_source], property: 'existing_user')
+ Gitlab::Tracking.event(::Members::CreateService.name, 'create_member', label: params[:invite_source], property: 'existing_user', user: current_user)
else
render_validation_error!(member)
end
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index a13f2046291..5af270c4cff 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -11,7 +11,7 @@
# * test: TEST_DISABLED
# * code_quality: CODE_QUALITY_DISABLED
# * license_management: LICENSE_MANAGEMENT_DISABLED
-# * performance: PERFORMANCE_DISABLED
+# * browser_performance: PERFORMANCE_DISABLED
# * load_performance: LOAD_PERFORMANCE_DISABLED
# * sast: SAST_DISABLED
# * secret_detection: SECRET_DETECTION_DISABLED
diff --git a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
index 01907ef9e2e..56899614cc6 100644
--- a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
@@ -1,6 +1,6 @@
# Read more about the feature here: https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html
-performance:
+browser_performance:
stage: performance
image: docker:19.03.12
allow_failure: true
@@ -72,6 +72,6 @@ performance:
rules:
- if: '$CI_KUBERNETES_ACTIVE == null || $CI_KUBERNETES_ACTIVE == ""'
when: never
- - if: '$PERFORMANCE_DISABLED'
+ - if: '$BROWSER_PERFORMANCE_DISABLED'
when: never
- if: '$CI_COMMIT_TAG || $CI_COMMIT_BRANCH'
diff --git a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml
index 5216a46745c..56899614cc6 100644
--- a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml
@@ -72,6 +72,6 @@ browser_performance:
rules:
- if: '$CI_KUBERNETES_ACTIVE == null || $CI_KUBERNETES_ACTIVE == ""'
when: never
- - if: '$PERFORMANCE_DISABLED'
+ - if: '$BROWSER_PERFORMANCE_DISABLED'
when: never
- if: '$CI_COMMIT_TAG || $CI_COMMIT_BRANCH'
diff --git a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
index b29342216fc..48e877684f6 100644
--- a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
@@ -7,7 +7,7 @@ code_quality:
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
- CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/ci-cd/codequality:0.85.23"
+ CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/ci-cd/codequality:0.85.24"
needs: []
script:
- export SOURCE_CODE=$PWD
diff --git a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
index 275364afae4..1bdaaeede43 100644
--- a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
@@ -1,6 +1,6 @@
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/ruby/tags/
-image: "ruby:2.5"
+image: ruby:latest
# Pick zero or more services to be used on all builds.
# Only needed when using a docker container to run your tests in.
diff --git a/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml b/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml
index 404d4a4c6db..f0621165f8a 100644
--- a/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml
@@ -6,7 +6,7 @@ stages:
- deploy
- performance
-performance:
+browser_performance:
stage: performance
image: docker:git
variables:
diff --git a/lib/gitlab/ci/yaml_processor/result.rb b/lib/gitlab/ci/yaml_processor/result.rb
index 460fad16d5c..15cc0c28296 100644
--- a/lib/gitlab/ci/yaml_processor/result.rb
+++ b/lib/gitlab/ci/yaml_processor/result.rb
@@ -112,7 +112,9 @@ module Gitlab
end
def yaml_variables_for(job_name)
- job = jobs.fetch(job_name)
+ job = jobs[job_name]
+
+ return [] unless job
Gitlab::Ci::Variables::Helpers.inherit_yaml_variables(
from: root_variables,
@@ -122,9 +124,7 @@ module Gitlab
end
def stage_for(job_name)
- job = jobs.fetch(job_name)
-
- job[:stage]
+ jobs.dig(job_name, :stage)
end
private
diff --git a/lib/gitlab/data_builder/build.rb b/lib/gitlab/data_builder/build.rb
index 4c31f986be5..91e6fc11a53 100644
--- a/lib/gitlab/data_builder/build.rb
+++ b/lib/gitlab/data_builder/build.rb
@@ -83,7 +83,9 @@ module Gitlab
{
id: runner.id,
description: runner.description,
+ runner_type: runner.runner_type,
active: runner.active?,
+ is_shared: runner.instance_type?,
tags: runner.tags&.map(&:name)
}
end
diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb
index 766eaf54afe..4d70e3949dd 100644
--- a/lib/gitlab/data_builder/pipeline.rb
+++ b/lib/gitlab/data_builder/pipeline.rb
@@ -79,7 +79,9 @@ module Gitlab
{
id: runner.id,
description: runner.description,
+ runner_type: runner.runner_type,
active: runner.active?,
+ is_shared: runner.instance_type?,
tags: runner.tags&.map(&:name)
}
end
diff --git a/lib/gitlab/markdown_cache/field_data.rb b/lib/gitlab/markdown_cache/field_data.rb
index 14622c0f186..75364570640 100644
--- a/lib/gitlab/markdown_cache/field_data.rb
+++ b/lib/gitlab/markdown_cache/field_data.rb
@@ -9,7 +9,7 @@ module Gitlab
@data = {}
end
- delegate :[], :[]=, to: :@data
+ delegate :[], :[]=, :key?, to: :@data
def markdown_fields
@data.keys
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index 399405953a7..6145e0f3540 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -240,9 +240,7 @@ namespace :gitlab do
desc 'Run migrations with instrumentation'
task migration_testing: :environment do
result_dir = Gitlab::Database::Migrations::Instrumentation::RESULT_DIR
- raise "Directory exists already, won't overwrite: #{result_dir}" if File.exist?(result_dir)
-
- Dir.mkdir(result_dir)
+ FileUtils.mkdir_p(result_dir)
verbose_was = ActiveRecord::Migration.verbose
ActiveRecord::Migration.verbose = true
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 717a74b3a89..3f2945ecdf0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8319,42 +8319,9 @@ msgstr ""
msgid "ComplianceFramework|Edit Compliance Framework"
msgstr ""
-msgid "ComplianceFramework|GDPR"
-msgstr ""
-
-msgid "ComplianceFramework|GDPR - General Data Protection Regulation"
-msgstr ""
-
-msgid "ComplianceFramework|HIPAA"
-msgstr ""
-
-msgid "ComplianceFramework|HIPAA - Health Insurance Portability and Accountability Act"
-msgstr ""
-
msgid "ComplianceFramework|New Compliance Framework"
msgstr ""
-msgid "ComplianceFramework|PCI-DSS"
-msgstr ""
-
-msgid "ComplianceFramework|PCI-DSS - Payment Card Industry-Data Security Standard"
-msgstr ""
-
-msgid "ComplianceFramework|SOC 2"
-msgstr ""
-
-msgid "ComplianceFramework|SOC 2 - Service Organization Control 2"
-msgstr ""
-
-msgid "ComplianceFramework|SOX"
-msgstr ""
-
-msgid "ComplianceFramework|SOX - Sarbanes-Oxley"
-msgstr ""
-
-msgid "ComplianceFramework|This project is regulated by %{framework}."
-msgstr ""
-
msgid "Component"
msgstr ""
@@ -39282,9 +39249,6 @@ msgstr ""
msgid "must be greater than start date"
msgstr ""
-msgid "must contain only valid frameworks"
-msgstr ""
-
msgid "my-awesome-group"
msgstr ""
diff --git a/qa/qa/specs/features/browser_ui/8_monitor/cluster_with_prometheus.rb b/qa/qa/specs/features/browser_ui/8_monitor/cluster_with_prometheus.rb
index ed0064e8b6f..19e49400d5e 100644
--- a/qa/qa/specs/features/browser_ui/8_monitor/cluster_with_prometheus.rb
+++ b/qa/qa/specs/features/browser_ui/8_monitor/cluster_with_prometheus.rb
@@ -17,7 +17,7 @@ module QA
%w[
CODE_QUALITY_DISABLED TEST_DISABLED LICENSE_MANAGEMENT_DISABLED
SAST_DISABLED DAST_DISABLED DEPENDENCY_SCANNING_DISABLED
- CONTAINER_SCANNING_DISABLED PERFORMANCE_DISABLED SECRET_DETECTION_DISABLED
+ CONTAINER_SCANNING_DISABLED BROWSER_PERFORMANCE_DISABLED SECRET_DETECTION_DISABLED
].each do |key|
Resource::CiVariable.fabricate_via_api! do |resource|
resource.project = @project
diff --git a/scripts/frontend/startup_css/startup_css_changed.sh b/scripts/frontend/startup_css/startup_css_changed.sh
index 2ae647557e4..f214e61cdfb 100755
--- a/scripts/frontend/startup_css/startup_css_changed.sh
+++ b/scripts/frontend/startup_css/startup_css_changed.sh
@@ -23,7 +23,7 @@ regenerating the Startup CSS files.
**What should I do now?**
-IMPORTANT: Please make sure to update your MR title with `[RUN AS-IF-FOSS]` and start a new MR pipeline
+IMPORTANT: Please make sure to update your MR title with "[RUN AS-IF-FOSS]" and start a new MR pipeline
To fix this job, consider one of the following options:
diff --git a/spec/controllers/confirmations_controller_spec.rb b/spec/controllers/confirmations_controller_spec.rb
index 49a39f257fe..c9a0ae981fc 100644
--- a/spec/controllers/confirmations_controller_spec.rb
+++ b/spec/controllers/confirmations_controller_spec.rb
@@ -12,7 +12,9 @@ RSpec.describe ConfirmationsController do
describe '#show' do
render_views
- subject { get :show, params: { confirmation_token: confirmation_token } }
+ def perform_request
+ get :show, params: { confirmation_token: confirmation_token }
+ end
context 'user is already confirmed' do
let_it_be_with_reload(:user) { create(:user, :unconfirmed) }
@@ -20,20 +22,37 @@ RSpec.describe ConfirmationsController do
before do
user.confirm
- subject
end
it 'renders `new`' do
+ perform_request
+
expect(response).to render_template(:new)
end
it 'displays an error message' do
+ perform_request
+
expect(response.body).to include('Email was already confirmed, please try signing in')
end
it 'does not display the email of the user' do
+ perform_request
+
expect(response.body).not_to include(user.email)
end
+
+ it 'sets the username and caller_id in the context' do
+ expect(controller).to receive(:show).and_wrap_original do |m, *args|
+ m.call(*args)
+
+ expect(Gitlab::ApplicationContext.current)
+ .to include('meta.user' => user.username,
+ 'meta.caller_id' => 'ConfirmationsController#show')
+ end
+
+ perform_request
+ end
end
context 'user accesses the link after the expiry of confirmation token has passed' do
@@ -42,39 +61,64 @@ RSpec.describe ConfirmationsController do
before do
allow(Devise).to receive(:confirm_within).and_return(1.day)
-
- travel_to(3.days.from_now) do
- subject
- end
end
it 'renders `new`' do
+ travel_to(3.days.from_now) { perform_request }
+
expect(response).to render_template(:new)
end
it 'displays an error message' do
+ travel_to(3.days.from_now) { perform_request }
+
expect(response.body).to include('Email needs to be confirmed within 1 day, please request a new one below')
end
it 'does not display the email of the user' do
+ travel_to(3.days.from_now) { perform_request }
+
expect(response.body).not_to include(user.email)
end
+
+ it 'sets the username and caller_id in the context' do
+ expect(controller).to receive(:show).and_wrap_original do |m, *args|
+ m.call(*args)
+
+ expect(Gitlab::ApplicationContext.current)
+ .to include('meta.user' => user.username,
+ 'meta.caller_id' => 'ConfirmationsController#show')
+ end
+
+ travel_to(3.days.from_now) { perform_request }
+ end
end
context 'with an invalid confirmation token' do
let(:confirmation_token) { 'invalid_confirmation_token' }
- before do
- subject
- end
-
it 'renders `new`' do
+ perform_request
+
expect(response).to render_template(:new)
end
it 'displays an error message' do
+ perform_request
+
expect(response.body).to include('Confirmation token is invalid')
end
+
+ it 'sets the the caller_id in the context' do
+ expect(controller).to receive(:show).and_wrap_original do |m, *args|
+ expect(Gitlab::ApplicationContext.current)
+ .to include('meta.caller_id' => 'ConfirmationsController#show')
+
+ m.call(*args)
+ end
+
+ perform_request
+ end
end
end
end
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index 4a47a4a2a53..9a142559fca 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -293,6 +293,18 @@ RSpec.describe OmniauthCallbacksController, type: :controller do
expect(request.env['warden']).to be_authenticated
end
+
+ it 'sets the username and caller_id in the context' do
+ expect(controller).to receive(:atlassian_oauth2).and_wrap_original do |m, *args|
+ m.call(*args)
+
+ expect(Gitlab::ApplicationContext.current)
+ .to include('meta.user' => user.username,
+ 'meta.caller_id' => 'OmniauthCallbacksController#atlassian_oauth2')
+ end
+
+ post :atlassian_oauth2
+ end
end
context 'for a new user' do
@@ -454,6 +466,18 @@ RSpec.describe OmniauthCallbacksController, type: :controller do
it 'doesn\'t link a new identity to the user' do
expect { post :saml, params: { SAMLResponse: mock_saml_response } }.not_to change { user.identities.count }
end
+
+ it 'sets the username and caller_id in the context' do
+ expect(controller).to receive(:saml).and_wrap_original do |m, *args|
+ m.call(*args)
+
+ expect(Gitlab::ApplicationContext.current)
+ .to include('meta.user' => user.username,
+ 'meta.caller_id' => 'OmniauthCallbacksController#saml')
+ end
+
+ post :saml, params: { SAMLResponse: mock_saml_response }
+ end
end
end
diff --git a/spec/controllers/passwords_controller_spec.rb b/spec/controllers/passwords_controller_spec.rb
index e9883107456..08d68d7cec8 100644
--- a/spec/controllers/passwords_controller_spec.rb
+++ b/spec/controllers/passwords_controller_spec.rb
@@ -77,6 +77,18 @@ RSpec.describe PasswordsController do
expect(user.password_expires_at).not_to be_nil
end
end
+
+ it 'sets the username and caller_id in the context' do
+ expect(controller).to receive(:update).and_wrap_original do |m, *args|
+ m.call(*args)
+
+ expect(Gitlab::ApplicationContext.current)
+ .to include('meta.user' => user.username,
+ 'meta.caller_id' => 'PasswordsController#update')
+ end
+
+ subject
+ end
end
end
end
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index ff73c0aafe8..81486c310d4 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -434,6 +434,18 @@ RSpec.describe RegistrationsController do
expect(User.last.last_name).to eq(base_user_params[:last_name])
expect(User.last.name).to eq("#{base_user_params[:first_name]} #{base_user_params[:last_name]}")
end
+
+ it 'sets the username and caller_id in the context' do
+ expect(controller).to receive(:create).and_wrap_original do |m, *args|
+ m.call(*args)
+
+ expect(Gitlab::ApplicationContext.current)
+ .to include('meta.user' => base_user_params[:username],
+ 'meta.caller_id' => 'RegistrationsController#create')
+ end
+
+ subject
+ end
end
describe '#destroy' do
@@ -525,5 +537,17 @@ RSpec.describe RegistrationsController do
end
end
end
+
+ it 'sets the username and caller_id in the context' do
+ expect(controller).to receive(:destroy).and_wrap_original do |m, *args|
+ m.call(*args)
+
+ expect(Gitlab::ApplicationContext.current)
+ .to include('meta.user' => user.username,
+ 'meta.caller_id' => 'RegistrationsController#destroy')
+ end
+
+ post :destroy
+ end
end
end
diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb
index c5e6479ec51..eef92b5839d 100644
--- a/spec/features/groups/members/manage_members_spec.rb
+++ b/spec/features/groups/members/manage_members_spec.rb
@@ -3,20 +3,19 @@
require 'spec_helper'
RSpec.describe 'Groups > Members > Manage members' do
- include Select2Helper
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
- let(:user1) { create(:user, name: 'John Doe') }
- let(:user2) { create(:user, name: 'Mary Jane') }
- let(:group) { create(:group) }
+ let_it_be(:user1) { create(:user, name: 'John Doe') }
+ let_it_be(:user2) { create(:user, name: 'Mary Jane') }
+ let_it_be(:group) { create(:group) }
before do
sign_in(user1)
end
shared_examples 'includes the correct Invite link' do |should_include, should_not_include|
- it 'includes either the form or the modal trigger' do
+ it 'includes either the form or the modal trigger', :aggregate_failures do
group.add_owner(user1)
visit group_group_members_path(group)
@@ -27,12 +26,12 @@ RSpec.describe 'Groups > Members > Manage members' do
end
shared_examples 'does not include either invite modal or either invite form' do
- it 'does not include either of the invite members or invite group modal buttons' do
+ it 'does not include either of the invite members or invite group modal buttons', :aggregate_failures do
expect(page).not_to have_selector '.js-invite-members-modal'
expect(page).not_to have_selector '.js-invite-group-modal'
end
- it 'does not include either of the invite users or invite group forms' do
+ it 'does not include either of the invite users or invite group forms', :aggregate_failures do
expect(page).not_to have_selector '.invite-users-form'
expect(page).not_to have_selector '.invite-group-form'
end
@@ -66,7 +65,7 @@ RSpec.describe 'Groups > Members > Manage members' do
end
end
- it 'add user to group', :js do
+ it 'add user to group', :js, :snowplow, :aggregate_failures do
group.add_owner(user1)
visit group_group_members_path(group)
@@ -77,6 +76,13 @@ RSpec.describe 'Groups > Members > Manage members' do
expect(page).to have_content(user2.name)
expect(page).to have_button('Reporter')
end
+
+ expect_snowplow_event(
+ category: 'Members::CreateService',
+ action: 'create_member',
+ label: 'unknown',
+ property: 'existing_user'
+ )
end
it 'do not disclose email addresses', :js do
@@ -143,11 +149,13 @@ RSpec.describe 'Groups > Members > Manage members' do
wait_for_requests
- expect(page).not_to have_content(user2.name)
- expect(group.users).not_to include(user2)
+ aggregate_failures do
+ expect(page).not_to have_content(user2.name)
+ expect(group.users).not_to include(user2)
+ end
end
- it 'add yourself to group when already an owner', :js do
+ it 'add yourself to group when already an owner', :js, :aggregate_failures do
group.add_owner(user1)
visit group_group_members_path(group)
@@ -160,7 +168,7 @@ RSpec.describe 'Groups > Members > Manage members' do
end
end
- it 'invite user to group', :js do
+ it 'invite user to group', :js, :snowplow do
group.add_owner(user1)
visit group_group_members_path(group)
@@ -170,14 +178,23 @@ RSpec.describe 'Groups > Members > Manage members' do
expect(page).to have_link 'Invited'
click_link 'Invited'
- page.within(members_table) do
- expect(page).to have_content('test@example.com')
- expect(page).to have_content('Invited')
- expect(page).to have_button('Reporter')
+ aggregate_failures do
+ page.within(members_table) do
+ expect(page).to have_content('test@example.com')
+ expect(page).to have_content('Invited')
+ expect(page).to have_button('Reporter')
+ end
+
+ expect_snowplow_event(
+ category: 'Members::InviteService',
+ action: 'create_member',
+ label: 'unknown',
+ property: 'net_new_user'
+ )
end
end
- context 'as a guest', :js do
+ context 'when user is a guest' do
before do
group.add_guest(user1)
group.add_developer(user2)
@@ -187,7 +204,7 @@ RSpec.describe 'Groups > Members > Manage members' do
it_behaves_like 'does not include either invite modal or either invite form'
- it 'does not include a button on the members page list to manage or remove the existing member', :js do
+ it 'does not include a button on the members page list to manage or remove the existing member', :js, :aggregate_failures do
page.within(second_row) do
# Can not modify user2 role
expect(page).not_to have_button 'Developer'
@@ -198,7 +215,7 @@ RSpec.describe 'Groups > Members > Manage members' do
end
end
- context 'As a guest when the :invite_members_group_modal feature flag is disabled', :js do
+ context 'when user is a guest and the :invite_members_group_modal feature flag is disabled' do
before do
stub_feature_flags(invite_members_group_modal: false)
group.add_guest(user1)
@@ -209,7 +226,7 @@ RSpec.describe 'Groups > Members > Manage members' do
it_behaves_like 'does not include either invite modal or either invite form'
- it 'does not include a button on the members page list to manage or remove the existing member', :js do
+ it 'does not include a button on the members page list to manage or remove the existing member', :js, :aggregate_failures do
page.within(second_row) do
# Can not modify user2 role
expect(page).not_to have_button 'Developer'
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
index 42d7f658455..c3cc6b02439 100644
--- a/spec/features/projects/members/list_spec.rb
+++ b/spec/features/projects/members/list_spec.rb
@@ -6,17 +6,17 @@ RSpec.describe 'Project members list', :js do
include Spec::Support::Helpers::Features::MembersHelpers
include Spec::Support::Helpers::Features::InviteMembersModalHelper
- let(:user1) { create(:user, name: 'John Doe') }
- let(:user2) { create(:user, name: 'Mary Jane') }
- let(:group) { create(:group) }
- let(:project) { create(:project, :internal, namespace: group) }
+ let_it_be(:user1) { create(:user, name: 'John Doe') }
+ let_it_be(:user2) { create(:user, name: 'Mary Jane') }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :internal, namespace: group) }
before do
sign_in(user1)
group.add_owner(user1)
end
- it 'show members from project and group' do
+ it 'show members from project and group', :aggregate_failures do
project.add_developer(user2)
visit_members_page
@@ -25,7 +25,7 @@ RSpec.describe 'Project members list', :js do
expect(second_row).to have_content(user2.name)
end
- it 'show user once if member of both group and project' do
+ it 'show user once if member of both group and project', :aggregate_failures do
project.add_developer(user1)
visit_members_page
@@ -47,7 +47,7 @@ RSpec.describe 'Project members list', :js do
end
end
- it 'add user to project' do
+ it 'add user to project', :snowplow, :aggregate_failures do
visit_members_page
invite_member(user2.name, role: 'Reporter')
@@ -55,9 +55,16 @@ RSpec.describe 'Project members list', :js do
page.within find_member_row(user2) do
expect(page).to have_button('Reporter')
end
+
+ expect_snowplow_event(
+ category: 'Members::CreateService',
+ action: 'create_member',
+ label: 'unknown',
+ property: 'existing_user'
+ )
end
- it 'uses ProjectMember access_level_roles for the invite members modal access option' do
+ it 'uses ProjectMember access_level_roles for the invite members modal access option', :aggregate_failures do
visit_members_page
click_on 'Invite members'
@@ -95,7 +102,7 @@ RSpec.describe 'Project members list', :js do
expect(members_table).not_to have_content(other_user.name)
end
- it 'invite user to project' do
+ it 'invite user to project', :snowplow, :aggregate_failures do
visit_members_page
invite_member('test@example.com', role: 'Reporter')
@@ -105,6 +112,13 @@ RSpec.describe 'Project members list', :js do
page.within find_invited_member_row('test@example.com') do
expect(page).to have_button('Reporter')
end
+
+ expect_snowplow_event(
+ category: 'Members::InviteService',
+ action: 'create_member',
+ label: 'unknown',
+ property: 'net_new_user'
+ )
end
context 'as a signed out visitor viewing a public project' do
@@ -128,7 +142,7 @@ RSpec.describe 'Project members list', :js do
project.add_maintainer(project_bot)
end
- it 'does not show form used to change roles and "Expiration date" or the remove user button' do
+ it 'does not show form used to change roles and "Expiration date" or the remove user button', :aggregate_failures do
visit_members_page
page.within find_member_row(project_bot) do
diff --git a/spec/features/projects/user_views_empty_project_spec.rb b/spec/features/projects/user_views_empty_project_spec.rb
index 3d4d9a7ea96..cce38456df9 100644
--- a/spec/features/projects/user_views_empty_project_spec.rb
+++ b/spec/features/projects/user_views_empty_project_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe 'User views an empty project' do
- let(:project) { create(:project, :empty_repo) }
- let(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :empty_repo) }
+ let_it_be(:user) { create(:user) }
shared_examples 'allowing push to default branch' do
it 'shows push-to-master instructions' do
@@ -14,17 +14,25 @@ RSpec.describe 'User views an empty project' do
end
end
- describe 'as a maintainer' do
+ context 'when user is a maintainer' do
before do
project.add_maintainer(user)
sign_in(user)
end
it_behaves_like 'allowing push to default branch'
+
+ it 'shows a link for inviting members and launches invite modal', :js do
+ visit project_path(project)
+
+ click_button 'Invite members'
+
+ expect(page).to have_content("You're inviting members to the")
+ end
end
- describe 'as an admin' do
- let(:user) { create(:user, :admin) }
+ context 'when user is an admin' do
+ let_it_be(:user) { create(:user, :admin) }
context 'when admin mode is enabled' do
before do
@@ -44,16 +52,17 @@ RSpec.describe 'User views an empty project' do
end
end
- describe 'as a developer' do
+ context 'when user is a developer' do
before do
project.add_developer(user)
sign_in(user)
end
- it 'does not show push-to-master instructions' do
+ it 'does not show push-to-master instructions nor invite members link', :aggregate_failures, :js do
visit project_path(project)
expect(page).not_to have_content('git push -u origin master')
+ expect(page).not_to have_button(text: 'Invite members')
end
end
end
diff --git a/spec/frontend/fixtures/runner.rb b/spec/frontend/fixtures/runner.rb
new file mode 100644
index 00000000000..49d657c69c6
--- /dev/null
+++ b/spec/frontend/fixtures/runner.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Runner (JavaScript fixtures)' do
+ include AdminModeHelper
+ include ApiHelpers
+ include JavaScriptFixturesHelpers
+ include GraphqlHelpers
+
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :repository, :public) }
+
+ let_it_be(:instance_runner) { create(:ci_runner, :instance, version: '1.0.0', revision: '123', description: 'Instance runner', ip_address: '127.0.0.1') }
+ let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', revision: '456', description: 'Group runner', ip_address: '127.0.0.1') }
+ let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project], active: false, version: '2.0.0', revision: '456', description: 'Project runner', ip_address: '127.0.0.1') }
+
+ query_path = 'runner/graphql/'
+ fixtures_path = 'graphql/runner/'
+
+ before(:all) do
+ clean_frontend_fixtures(fixtures_path)
+ end
+
+ after(:all) do
+ remove_repository(project)
+ end
+
+ before do
+ sign_in(admin)
+ enable_admin_mode!(admin)
+ end
+
+ describe GraphQL::Query, type: :request do
+ get_runners_query_name = 'get_runners.query.graphql'
+
+ let_it_be(:query) do
+ get_graphql_query_as_string("#{query_path}#{get_runners_query_name}", [
+ 'runner/graphql/runner_node.fragment.graphql',
+ 'graphql_shared/fragments/pageInfo.fragment.graphql'
+ ])
+ end
+
+ it "#{fixtures_path}#{get_runners_query_name}.json" do
+ post_graphql(query, current_user: admin, variables: {})
+
+ expect_graphql_errors_to_be_empty
+ end
+
+ it "#{fixtures_path}#{get_runners_query_name}.paginated.json" do
+ post_graphql(query, current_user: admin, variables: { first: 2 })
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
+ describe GraphQL::Query, type: :request do
+ get_runner_query_name = 'get_runner.query.graphql'
+
+ let_it_be(:query) { get_graphql_query_as_string("#{query_path}#{get_runner_query_name}") }
+
+ it "#{fixtures_path}#{get_runner_query_name}.json" do
+ post_graphql(query, current_user: admin, variables: {
+ id: instance_runner.to_global_id.to_s
+ })
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
+end
diff --git a/spec/frontend/invite_members/components/invite_members_modal_spec.js b/spec/frontend/invite_members/components/invite_members_modal_spec.js
index 7ed18775693..eabbea84234 100644
--- a/spec/frontend/invite_members/components/invite_members_modal_spec.js
+++ b/spec/frontend/invite_members/components/invite_members_modal_spec.js
@@ -15,6 +15,7 @@ const isProject = false;
const inviteeType = 'members';
const accessLevels = { Guest: 10, Reporter: 20, Developer: 30, Maintainer: 40, Owner: 50 };
const defaultAccessLevel = 10;
+const inviteSource = 'unknown';
const helpLink = 'https://example.com';
const user1 = { id: 1, name: 'Name One', username: 'one_1', avatar_url: '' };
@@ -173,6 +174,7 @@ describe('InviteMembersModal', () => {
user_id: '1',
access_level: defaultAccessLevel,
expires_at: undefined,
+ invite_source: inviteSource,
format: 'json',
};
@@ -245,6 +247,7 @@ describe('InviteMembersModal', () => {
access_level: defaultAccessLevel,
expires_at: undefined,
email: 'email@example.com',
+ invite_source: inviteSource,
format: 'json',
};
@@ -293,6 +296,7 @@ describe('InviteMembersModal', () => {
const postData = {
access_level: defaultAccessLevel,
expires_at: undefined,
+ invite_source: inviteSource,
format: 'json',
};
@@ -308,20 +312,39 @@ describe('InviteMembersModal', () => {
jest.spyOn(Api, 'addGroupMembersByUserId').mockResolvedValue({ data: postData });
jest.spyOn(wrapper.vm, 'showToastMessageSuccess');
jest.spyOn(wrapper.vm, 'trackInvite');
-
- clickInviteButton();
});
- it('calls Api inviteGroupMembersByEmail with the correct params', () => {
- expect(Api.inviteGroupMembersByEmail).toHaveBeenCalledWith(id, emailPostData);
- });
+ describe('when triggered from regular mounting', () => {
+ beforeEach(() => {
+ clickInviteButton();
+ });
- it('calls Api addGroupMembersByUserId with the correct params', () => {
- expect(Api.addGroupMembersByUserId).toHaveBeenCalledWith(id, idPostData);
+ it('calls Api inviteGroupMembersByEmail with the correct params', () => {
+ expect(Api.inviteGroupMembersByEmail).toHaveBeenCalledWith(id, emailPostData);
+ });
+
+ it('calls Api addGroupMembersByUserId with the correct params', () => {
+ expect(Api.addGroupMembersByUserId).toHaveBeenCalledWith(id, idPostData);
+ });
+
+ it('displays the successful toastMessage', () => {
+ expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
+ });
});
- it('displays the successful toastMessage', () => {
- expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
+ it('calls Apis with the invite source passed through to openModal', () => {
+ wrapper.vm.openModal({ inviteeType: 'members', source: '_invite_source_' });
+
+ clickInviteButton();
+
+ expect(Api.inviteGroupMembersByEmail).toHaveBeenCalledWith(id, {
+ ...emailPostData,
+ invite_source: '_invite_source_',
+ });
+ expect(Api.addGroupMembersByUserId).toHaveBeenCalledWith(id, {
+ ...idPostData,
+ invite_source: '_invite_source_',
+ });
});
});
@@ -403,18 +426,11 @@ describe('InviteMembersModal', () => {
});
describe('tracking', () => {
- const postData = {
- user_id: '1',
- access_level: defaultAccessLevel,
- expires_at: undefined,
- format: 'json',
- };
-
beforeEach(() => {
wrapper = createComponent({ newUsersToInvite: [user3] });
wrapper.vm.$toast = { show: jest.fn() };
- jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({ data: postData });
+ jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({});
});
it('tracks the invite', () => {
diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js
index 6ba6f993db1..254e2fc07b4 100644
--- a/spec/frontend/repository/components/table/row_spec.js
+++ b/spec/frontend/repository/components/table/row_spec.js
@@ -1,5 +1,6 @@
import { GlBadge, GlLink, GlIcon } from '@gitlab/ui';
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import TableRow from '~/repository/components/table/row.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import { FILE_SYMLINK_MODE } from '~/vue_shared/constants';
@@ -19,6 +20,9 @@ function factory(propsData = {}) {
projectPath: 'gitlab-org/gitlab-ce',
url: `https://test.com`,
},
+ directives: {
+ GlHoverLoad: createMockDirective(),
+ },
provide: {
glFeatures: { refactorBlobViewer: true },
},
@@ -34,6 +38,8 @@ function factory(propsData = {}) {
}
describe('Repository table row component', () => {
+ const findRouterLink = () => vm.find(RouterLinkStub);
+
afterEach(() => {
vm.destroy();
});
@@ -81,6 +87,21 @@ describe('Repository table row component', () => {
});
});
+ it('renders a gl-hover-load directive', () => {
+ factory({
+ id: '1',
+ sha: '123',
+ path: 'test',
+ type: 'blob',
+ currentPath: '/',
+ });
+
+ const hoverLoadDirective = getBinding(findRouterLink().element, 'gl-hover-load');
+
+ expect(hoverLoadDirective).not.toBeUndefined();
+ expect(hoverLoadDirective.value).toBeInstanceOf(Function);
+ });
+
it.each`
type | component | componentName
${'tree'} | ${RouterLinkStub} | ${'RouterLink'}
diff --git a/spec/frontend/runner/components/runner_list_spec.js b/spec/frontend/runner/components/runner_list_spec.js
index 09093c2ce64..d88d7b3fbee 100644
--- a/spec/frontend/runner/components/runner_list_spec.js
+++ b/spec/frontend/runner/components/runner_list_spec.js
@@ -68,7 +68,7 @@ describe('RunnerList', () => {
});
it('Displays a list of runners', () => {
- expect(findRows()).toHaveLength(2);
+ expect(findRows()).toHaveLength(3);
expect(findSkeletonLoader().exists()).toBe(false);
});
@@ -77,7 +77,7 @@ describe('RunnerList', () => {
const { id, description, version, ipAddress, shortSha } = mockRunners[0];
// Badges
- expect(findCell({ fieldKey: 'type' }).text()).toMatchInterpolatedText('shared locked');
+ expect(findCell({ fieldKey: 'type' }).text()).toMatchInterpolatedText('specific paused');
// Runner identifier
expect(findCell({ fieldKey: 'name' }).text()).toContain(
diff --git a/spec/frontend/runner/mock_data.js b/spec/frontend/runner/mock_data.js
index 744942bfa73..8f551feca6e 100644
--- a/spec/frontend/runner/mock_data.js
+++ b/spec/frontend/runner/mock_data.js
@@ -1,44 +1,6 @@
-export const runnersData = {
- data: {
- runners: {
- nodes: [
- {
- id: 'gid://gitlab/Ci::Runner/1',
- description: 'runner-1',
- runnerType: 'INSTANCE_TYPE',
- shortSha: '2P6oDVDm',
- version: '13.12.0',
- revision: '11223344',
- ipAddress: '127.0.0.1',
- active: true,
- locked: true,
- tagList: [],
- contactedAt: '2021-05-14T11:44:03Z',
- __typename: 'CiRunner',
- },
- {
- id: 'gid://gitlab/Ci::Runner/2',
- description: 'runner-2',
- runnerType: 'GROUP_TYPE',
- shortSha: 'dpSCAC31',
- version: '13.12.0',
- revision: '11223344',
- ipAddress: '127.0.0.1',
- active: true,
- locked: true,
- tagList: [],
- contactedAt: '2021-05-14T11:44:02Z',
- __typename: 'CiRunner',
- },
- ],
- pageInfo: {
- endCursor: 'GRAPHQL_END_CURSOR',
- startCursor: 'GRAPHQL_START_CURSOR',
- hasNextPage: true,
- hasPreviousPage: false,
- __typename: 'PageInfo',
- },
- __typename: 'CiRunnerConnection',
- },
- },
-};
+// Fixtures generated by: spec/frontend/fixtures/runner.rb
+export const runnersData = getJSONFixture('graphql/runner/get_runners.query.graphql.json');
+export const runnersDataPaginated = getJSONFixture(
+ 'graphql/runner/get_runners.query.graphql.paginated.json',
+);
+export const runnerData = getJSONFixture('graphql/runner/get_runner.query.graphql.json');
diff --git a/spec/frontend/runner/runner_detail/runner_details_app_spec.js b/spec/frontend/runner/runner_detail/runner_details_app_spec.js
index c61cb647ae6..d0bd701458d 100644
--- a/spec/frontend/runner/runner_detail/runner_details_app_spec.js
+++ b/spec/frontend/runner/runner_detail/runner_details_app_spec.js
@@ -3,12 +3,15 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
-import { INSTANCE_TYPE } from '~/runner/constants';
import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql';
import RunnerDetailsApp from '~/runner/runner_details/runner_details_app.vue';
-const mockRunnerId = '55';
+import { runnerData } from '../mock_data';
+
+const mockRunnerGraphqlId = runnerData.data.runner.id;
+const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`;
const localVue = createLocalVue();
localVue.use(VueApollo);
@@ -35,15 +38,7 @@ describe('RunnerDetailsApp', () => {
};
beforeEach(async () => {
- mockRunnerQuery = jest.fn().mockResolvedValue({
- data: {
- runner: {
- id: `gid://gitlab/Ci::Runner/${mockRunnerId}`,
- runnerType: INSTANCE_TYPE,
- __typename: 'CiRunner',
- },
- },
- });
+ mockRunnerQuery = jest.fn().mockResolvedValue(runnerData);
});
afterEach(() => {
@@ -54,13 +49,13 @@ describe('RunnerDetailsApp', () => {
it('expect GraphQL ID to be requested', async () => {
await createComponentWithApollo();
- expect(mockRunnerQuery).toHaveBeenCalledWith({ id: `gid://gitlab/Ci::Runner/${mockRunnerId}` });
+ expect(mockRunnerQuery).toHaveBeenCalledWith({ id: mockRunnerGraphqlId });
});
it('displays the runner id', async () => {
await createComponentWithApollo();
- expect(wrapper.text()).toContain('Runner #55');
+ expect(wrapper.text()).toContain(`Runner #${mockRunnerId}`);
});
it('displays the runner type', async () => {
diff --git a/spec/frontend/runner/runner_list/runner_list_app_spec.js b/spec/frontend/runner/runner_list/runner_list_app_spec.js
index e908e62db4f..dd913df7143 100644
--- a/spec/frontend/runner/runner_list/runner_list_app_spec.js
+++ b/spec/frontend/runner/runner_list/runner_list_app_spec.js
@@ -24,12 +24,10 @@ import {
import getRunnersQuery from '~/runner/graphql/get_runners.query.graphql';
import RunnerListApp from '~/runner/runner_list/runner_list_app.vue';
-import { runnersData } from '../mock_data';
+import { runnersData, runnersDataPaginated } from '../mock_data';
const mockRegistrationToken = 'MOCK_REGISTRATION_TOKEN';
const mockActiveRunnersCount = 2;
-const mocKRunners = runnersData.data.runners.nodes;
-const mockPageInfo = runnersData.data.runners.pageInfo;
jest.mock('@sentry/browser');
jest.mock('~/lib/utils/url_utility', () => ({
@@ -98,7 +96,7 @@ describe('RunnerListApp', () => {
});
it('shows the runners list', () => {
- expect(mocKRunners).toMatchObject(findRunnerList().props('runners'));
+ expect(runnersData.data.runners.nodes).toMatchObject(findRunnerList().props('runners'));
});
it('requests the runners with no filters', () => {
@@ -205,6 +203,8 @@ describe('RunnerListApp', () => {
describe('Pagination', () => {
beforeEach(() => {
+ mockRunnersQuery = jest.fn().mockResolvedValue(runnersDataPaginated);
+
createComponentWithApollo({ mountFn: mount });
});
@@ -225,13 +225,7 @@ describe('RunnerListApp', () => {
expect(mockRunnersQuery).toHaveBeenLastCalledWith({
sort: CREATED_DESC,
first: RUNNER_PAGE_SIZE,
- after: expect.any(String),
- });
-
- expect(mockRunnersQuery).toHaveBeenLastCalledWith({
- sort: CREATED_DESC,
- first: RUNNER_PAGE_SIZE,
- after: mockPageInfo.endCursor,
+ after: runnersDataPaginated.data.runners.pageInfo.endCursor,
});
});
});
diff --git a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
index a0a79dcad36..25705fd4260 100644
--- a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
@@ -54,14 +54,22 @@ module Gitlab
YAML
end
- subject(:yaml_variables_for) { result.yaml_variables_for(:job) }
+ let(:job_name) { :job }
- it do
+ subject(:yaml_variables_for) { result.yaml_variables_for(job_name) }
+
+ it 'returns calculated variables with root and job variables' do
is_expected.to match_array([
{ key: 'VAR1', value: 'value 11', public: true },
{ key: 'VAR2', value: 'value 2', public: true }
])
end
+
+ context 'when an absent job is sent' do
+ let(:job_name) { :invalid_job }
+
+ it { is_expected.to eq([]) }
+ end
end
describe '#stage_for' do
@@ -72,10 +80,16 @@ module Gitlab
YAML
end
- subject(:stage_for) { result.stage_for(:job) }
+ let(:job_name) { :job }
+
+ subject(:stage_for) { result.stage_for(job_name) }
+
+ it { is_expected.to eq('test') }
+
+ context 'when an absent job is sent' do
+ let(:job_name) { :invalid_job }
- it do
- is_expected.to eq('test')
+ it { is_expected.to be_nil }
end
end
end
diff --git a/spec/lib/gitlab/data_builder/build_spec.rb b/spec/lib/gitlab/data_builder/build_spec.rb
index a31e5a1d1e2..325fdb90929 100644
--- a/spec/lib/gitlab/data_builder/build_spec.rb
+++ b/spec/lib/gitlab/data_builder/build_spec.rb
@@ -47,6 +47,8 @@ RSpec.describe Gitlab::DataBuilder::Build do
it { expect(data[:runner][:id]).to eq(build.runner.id) }
it { expect(data[:runner][:tags]).to match_array(tag_names) }
it { expect(data[:runner][:description]).to eq(build.runner.description) }
+ it { expect(data[:runner][:runner_type]).to eq(build.runner.runner_type) }
+ it { expect(data[:runner][:is_shared]).to eq(build.runner.instance_type?) }
it { expect(data[:environment]).to be_nil }
context 'commit author_url' do
diff --git a/spec/lib/gitlab/data_builder/pipeline_spec.rb b/spec/lib/gitlab/data_builder/pipeline_spec.rb
index bec1e612c02..c05a044f0de 100644
--- a/spec/lib/gitlab/data_builder/pipeline_spec.rb
+++ b/spec/lib/gitlab/data_builder/pipeline_spec.rb
@@ -58,8 +58,10 @@ RSpec.describe Gitlab::DataBuilder::Pipeline do
it 'has runner attributes', :aggregate_failures do
expect(runner_data[:id]).to eq(ci_runner.id)
expect(runner_data[:description]).to eq(ci_runner.description)
+ expect(runner_data[:runner_type]).to eq(ci_runner.runner_type)
expect(runner_data[:active]).to eq(ci_runner.active)
expect(runner_data[:tags]).to match_array(tag_names)
+ expect(runner_data[:is_shared]).to eq(ci_runner.instance_type?)
end
end
diff --git a/spec/lib/gitlab/markdown_cache/field_data_spec.rb b/spec/lib/gitlab/markdown_cache/field_data_spec.rb
index 76d8cbe6b7d..6d4b57254f2 100644
--- a/spec/lib/gitlab/markdown_cache/field_data_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/field_data_spec.rb
@@ -12,4 +12,11 @@ RSpec.describe Gitlab::MarkdownCache::FieldData do
it 'translates a markdown field name into a html field name' do
expect(field_data.html_field(:description)).to eq("description_html")
end
+
+ describe '#key?' do
+ specify do
+ expect(field_data.key?(:description)).to be_truthy
+ expect(field_data.key?(:something_else)).to be_falsy
+ end
+ end
end
diff --git a/spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb b/spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb
new file mode 100644
index 00000000000..c457be79834
--- /dev/null
+++ b/spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_migration!('remove_builds_email_service_from_services')
+
+RSpec.describe RemoveBuildsEmailServiceFromServices do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:services) { table(:services) }
+ let(:namespace) { namespaces.create!(name: 'foo', path: 'bar') }
+ let(:project) { projects.create!(namespace_id: namespace.id) }
+
+ it 'correctly deletes `BuildsEmailService` services' do
+ services.create!(project_id: project.id, type: 'BuildsEmailService')
+ services.create!(project_id: project.id, type: 'OtherService')
+
+ expect(services.all.pluck(:type)).to match_array %w[BuildsEmailService OtherService]
+
+ migrate!
+
+ expect(services.all.pluck(:type)).to eq %w[OtherService]
+ end
+end
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index b0575b63c8a..372fc40afcc 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -408,6 +408,20 @@ RSpec.describe Member do
it { is_expected.not_to include @member_with_minimal_access }
end
+ describe '.without_invites_and_requests' do
+ subject { described_class.without_invites_and_requests.to_a }
+
+ it { is_expected.to include @owner }
+ it { is_expected.to include @maintainer }
+ it { is_expected.not_to include @invited_member }
+ it { is_expected.to include @accepted_invite_member }
+ it { is_expected.not_to include @requested_member }
+ it { is_expected.to include @accepted_request_member }
+ it { is_expected.to include @blocked_maintainer }
+ it { is_expected.to include @blocked_developer }
+ it { is_expected.not_to include @member_with_minimal_access }
+ end
+
describe '.connected_to_user' do
subject { described_class.connected_to_user.to_a }
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index 1727bc830fc..82fb4440429 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -137,11 +137,11 @@ RSpec.describe API::Ci::Runners do
get api('/runners/all', admin)
expect(json_response).to match_array [
- a_hash_including('description' => 'Project runner'),
- a_hash_including('description' => 'Two projects runner'),
- a_hash_including('description' => 'Group runner A'),
- a_hash_including('description' => 'Group runner B'),
- a_hash_including('description' => 'Shared runner')
+ a_hash_including('description' => 'Project runner', 'is_shared' => false, 'runner_type' => 'project_type'),
+ a_hash_including('description' => 'Two projects runner', 'is_shared' => false, 'runner_type' => 'project_type'),
+ a_hash_including('description' => 'Group runner A', 'is_shared' => false, 'runner_type' => 'group_type'),
+ a_hash_including('description' => 'Group runner B', 'is_shared' => false, 'runner_type' => 'group_type'),
+ a_hash_including('description' => 'Shared runner', 'is_shared' => true, 'runner_type' => 'instance_type')
]
end
diff --git a/spec/requests/api/graphql/group/group_members_spec.rb b/spec/requests/api/graphql/group/group_members_spec.rb
index 452610ab18f..31cb0393d7f 100644
--- a/spec/requests/api/graphql/group/group_members_spec.rb
+++ b/spec/requests/api/graphql/group/group_members_spec.rb
@@ -14,6 +14,23 @@ RSpec.describe 'getting group members information' do
[user_1, user_2].each { |user| parent_group.add_guest(user) }
end
+ context 'when a member is invited only via email' do
+ before do
+ create(:group_member, :invited, source: parent_group)
+ end
+
+ it 'returns null in the user field' do
+ fetch_members(group: parent_group, args: { relations: [:DIRECT] })
+
+ expect(graphql_errors).to be_nil
+ expect(graphql_data_at(:group, :group_members, :edges, :node)).to contain_exactly(
+ { 'user' => { 'id' => global_id_of(user_1) } },
+ { 'user' => { 'id' => global_id_of(user_2) } },
+ 'user' => nil
+ )
+ end
+ end
+
context 'when the request is correct' do
it_behaves_like 'a working graphql query' do
before do
diff --git a/spec/requests/api/graphql/project/project_members_spec.rb b/spec/requests/api/graphql/project/project_members_spec.rb
index c08bb8dc0a0..466464f600c 100644
--- a/spec/requests/api/graphql/project/project_members_spec.rb
+++ b/spec/requests/api/graphql/project/project_members_spec.rb
@@ -50,6 +50,20 @@ RSpec.describe 'getting project members information' do
invited_group.add_guest(invited_user)
end
+ context 'when a member is invited only via email and current_user is a maintainer' do
+ before do
+ parent_project.add_maintainer(user)
+ create(:project_member, :invited, source: parent_project)
+ end
+
+ it 'returns null in the user field' do
+ fetch_members(project: parent_project, args: { relations: [:DIRECT] })
+
+ expect(graphql_errors).to be_nil
+ expect(graphql_data_at(:project, :project_members, :edges, :node)).to contain_exactly({ 'user' => { 'id' => global_id_of(user) } }, 'user' => nil)
+ end
+ end
+
it 'returns direct members' do
fetch_members(project: child_project, args: { relations: [:DIRECT] })
diff --git a/spec/requests/api/invitations_spec.rb b/spec/requests/api/invitations_spec.rb
index a6eae2d5963..e4f394eed0e 100644
--- a/spec/requests/api/invitations_spec.rb
+++ b/spec/requests/api/invitations_spec.rb
@@ -162,7 +162,8 @@ RSpec.describe API::Invitations do
category: 'Members::InviteService',
action: 'create_member',
label: 'api',
- property: 'net_new_user'
+ property: 'net_new_user',
+ user: maintainer
)
end
@@ -173,7 +174,8 @@ RSpec.describe API::Invitations do
category: 'Members::InviteService',
action: 'create_member',
label: '_invite_source_',
- property: 'net_new_user'
+ property: 'net_new_user',
+ user: maintainer
)
end
end
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 7f04d169710..93fa36205ab 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -266,7 +266,8 @@ RSpec.describe API::Members do
category: 'Members::CreateService',
action: 'create_member',
label: 'api',
- property: 'existing_user'
+ property: 'existing_user',
+ user: maintainer
)
end
@@ -278,7 +279,8 @@ RSpec.describe API::Members do
category: 'Members::CreateService',
action: 'create_member',
label: '_invite_source_',
- property: 'existing_user'
+ property: 'existing_user',
+ user: maintainer
)
end
end
@@ -321,7 +323,8 @@ RSpec.describe API::Members do
category: 'Members::CreateService',
action: 'create_member',
label: 'api',
- property: 'existing_user'
+ property: 'existing_user',
+ user: maintainer
)
end
@@ -333,7 +336,8 @@ RSpec.describe API::Members do
category: 'Members::CreateService',
action: 'create_member',
label: '_invite_source_',
- property: 'existing_user'
+ property: 'existing_user',
+ user: maintainer
)
end
end
diff --git a/spec/services/issues/zoom_link_service_spec.rb b/spec/services/issues/zoom_link_service_spec.rb
index 19db892fcae..d662d9fa978 100644
--- a/spec/services/issues/zoom_link_service_spec.rb
+++ b/spec/services/issues/zoom_link_service_spec.rb
@@ -53,7 +53,10 @@ RSpec.describe Issues::ZoomLinkService do
category: 'IncidentManagement::ZoomIntegration',
action: 'add_zoom_meeting',
label: 'Issue ID',
- value: issue.id
+ value: issue.id,
+ user: user,
+ project: project,
+ namespace: project.namespace
)
end
@@ -192,7 +195,10 @@ RSpec.describe Issues::ZoomLinkService do
category: 'IncidentManagement::ZoomIntegration',
action: 'remove_zoom_meeting',
label: 'Issue ID',
- value: issue.id
+ value: issue.id,
+ user: user,
+ project: project,
+ namespace: project.namespace
)
end
end
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
index 523da9e522a..803b0695345 100644
--- a/spec/services/members/create_service_spec.rb
+++ b/spec/services/members/create_service_spec.rb
@@ -93,7 +93,8 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
category: described_class.name,
action: 'create_member',
label: 'unknown',
- property: 'existing_user'
+ property: 'existing_user',
+ user: user
)
end
end
@@ -108,7 +109,8 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
category: described_class.name,
action: 'create_member',
label: '_invite_source_',
- property: 'existing_user'
+ property: 'existing_user',
+ user: user
)
end
end
@@ -123,7 +125,8 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
category: described_class.name,
action: 'create_member',
label: 'unknown',
- property: 'net_new_user'
+ property: 'net_new_user',
+ user: user
)
end
end
diff --git a/spec/services/security/ci_configuration/sast_parser_service_spec.rb b/spec/services/security/ci_configuration/sast_parser_service_spec.rb
index cc6543cddc2..9ebc6be78e6 100644
--- a/spec/services/security/ci_configuration/sast_parser_service_spec.rb
+++ b/spec/services/security/ci_configuration/sast_parser_service_spec.rb
@@ -67,6 +67,23 @@ RSpec.describe Security::CiConfiguration::SastParserService do
expect(sast_brakeman_level['value']).to eql('1')
end
end
+
+ context 'when .gitlab-ci.yml does not include the sast job' do
+ before do
+ allow(project.repository).to receive(:blob_data_at).and_return(
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ )
+ end
+
+ it 'populates the current values with the default values' do
+ expect(secure_analyzers_prefix['value']).to eql('registry.gitlab.com/gitlab-org/security-products/analyzers')
+ expect(sast_excluded_paths['value']).to eql('spec, test, tests, tmp')
+ expect(sast_pipeline_stage['value']).to eql('test')
+ expect(sast_search_max_depth['value']).to eql('4')
+ expect(brakeman['enabled']).to be(true)
+ expect(sast_brakeman_level['value']).to eql('1')
+ end
+ end
end
end
end
diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb
index c4623061944..1df7e2d4e98 100644
--- a/spec/tasks/gitlab/db_rake_spec.rb
+++ b/spec/tasks/gitlab/db_rake_spec.rb
@@ -306,7 +306,7 @@ RSpec.describe 'gitlab:db namespace rake task' do
let(:all_migrations) { [double('migration1', version: 1), pending_migration] }
let(:pending_migration) { double('migration2', version: 2) }
let(:filename) { Gitlab::Database::Migrations::Instrumentation::STATS_FILENAME }
- let!(:directory) { Dir.mktmpdir }
+ let(:result_dir) { Dir.mktmpdir }
let(:observations) { %w[some data] }
before do
@@ -316,19 +316,17 @@ RSpec.describe 'gitlab:db namespace rake task' do
allow(instrumentation).to receive(:observe).and_yield
- allow(Dir).to receive(:mkdir)
- allow(File).to receive(:exist?).with(directory).and_return(false)
- stub_const('Gitlab::Database::Migrations::Instrumentation::RESULT_DIR', directory)
+ stub_const('Gitlab::Database::Migrations::Instrumentation::RESULT_DIR', result_dir)
end
after do
- FileUtils.rm_rf([directory])
+ FileUtils.rm_rf(result_dir)
end
- it 'fails when the directory already exists' do
- expect(File).to receive(:exist?).with(directory).and_return(true)
+ it 'creates result directory when one does not exist' do
+ FileUtils.rm_rf(result_dir)
- expect { subject }.to raise_error(/Directory exists/)
+ expect { subject }.to change { Dir.exist?(result_dir) }.from(false).to(true)
end
it 'instruments the pending migration' do
@@ -346,7 +344,7 @@ RSpec.describe 'gitlab:db namespace rake task' do
it 'writes observations out to JSON file' do
subject
- expect(File.read(File.join(directory, filename))).to eq(observations.to_json)
+ expect(File.read(File.join(result_dir, filename))).to eq(observations.to_json)
end
end
diff --git a/spec/views/projects/empty.html.haml_spec.rb b/spec/views/projects/empty.html.haml_spec.rb
index 09fb9da598e..7fa95507f75 100644
--- a/spec/views/projects/empty.html.haml_spec.rb
+++ b/spec/views/projects/empty.html.haml_spec.rb
@@ -57,20 +57,24 @@ RSpec.describe 'projects/empty' do
render
expect(rendered).to have_selector('[data-track-event=render]')
- expect(rendered).to have_selector('[data-track-label=invite_members_empty_project]', count: 2)
+ expect(rendered).to have_selector('[data-track-label=invite_members_empty_project]')
expect(rendered).to have_content('Invite your team')
expect(rendered).to have_content('Add members to this project and start collaborating with your team.')
- expect(rendered).to have_link('Invite members', href: project_project_members_path(project, sort: :access_level_desc))
- expect(rendered).to have_selector('[data-track-event=click_button]')
+ expect(rendered).to have_selector('.js-invite-members-trigger')
+ expect(rendered).to have_selector('.js-invite-members-modal')
+ expect(rendered).to have_selector('[data-label=invite_members_empty_project]')
+ expect(rendered).to have_selector('[data-event=click_button]')
end
context 'when user does not have permissions to invite members' do
let(:can_import_members) { false }
- it 'does not show invite member info' do
+ it 'does not show invite member info', :aggregate_failures do
render
expect(rendered).not_to have_content('Invite your team')
+ expect(rendered).not_to have_selector('.js-invite-members-trigger')
+ expect(rendered).not_to have_selector('.js-invite-members-modal')
end
end
end