Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
de8e5077c3
commit
79f98200f8
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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="{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
query getBlobInfo($projectPath: ID!, $filePath: String!) {
|
||||
project(fullPath: $projectPath) {
|
||||
id
|
||||
repository {
|
||||
blobs(paths: [$filePath]) {
|
||||
nodes {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
fb02e0fee2760dad203b54d81c342dbf1461b3010503cab05da1eb14ab5d33da
|
||||
|
|
@ -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`
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
||||

|
||||
|
||||
The following job is able to authenticate using the `myproject-production` role and read secrets under `/secret/myproject/production/`:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ stages:
|
|||
- deploy
|
||||
- performance
|
||||
|
||||
performance:
|
||||
browser_performance:
|
||||
stage: performance
|
||||
image: docker:git
|
||||
variables:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module Gitlab
|
|||
@data = {}
|
||||
end
|
||||
|
||||
delegate :[], :[]=, to: :@data
|
||||
delegate :[], :[]=, :key?, to: :@data
|
||||
|
||||
def markdown_fields
|
||||
@data.keys
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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');
|
||||
});
|
||||
|
||||
describe('when triggered from regular mounting', () => {
|
||||
beforeEach(() => {
|
||||
clickInviteButton();
|
||||
});
|
||||
|
||||
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('calls Apis with the invite source passed through to openModal', () => {
|
||||
wrapper.vm.openModal({ inviteeType: 'members', source: '_invite_source_' });
|
||||
|
||||
clickInviteButton();
|
||||
});
|
||||
|
||||
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();
|
||||
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', () => {
|
||||
|
|
|
|||
|
|
@ -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'}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
||||
it do
|
||||
is_expected.to eq('test')
|
||||
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 { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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] })
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue