Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
da646aac6c
commit
462b603802
|
@ -18,16 +18,6 @@ Rails/SaveBang:
|
|||
- ee/spec/models/visible_approvable_spec.rb
|
||||
- ee/spec/models/vulnerabilities/feedback_spec.rb
|
||||
- ee/spec/models/vulnerabilities/issue_link_spec.rb
|
||||
- ee/spec/services/ee/merge_requests/update_service_spec.rb
|
||||
- ee/spec/services/ee/notes/quick_actions_service_spec.rb
|
||||
- ee/spec/services/ee/notification_service_spec.rb
|
||||
- ee/spec/services/epic_links/create_service_spec.rb
|
||||
- ee/spec/services/epics/close_service_spec.rb
|
||||
- ee/spec/services/epics/issue_promote_service_spec.rb
|
||||
- ee/spec/services/epics/reopen_service_spec.rb
|
||||
- ee/spec/services/epics/tree_reorder_service_spec.rb
|
||||
- ee/spec/services/epics/update_dates_service_spec.rb
|
||||
- ee/spec/services/epics/update_service_spec.rb
|
||||
- ee/spec/services/geo/blob_verification_secondary_service_spec.rb
|
||||
- ee/spec/services/geo/files_expire_service_spec.rb
|
||||
- ee/spec/services/geo/metrics_update_service_spec.rb
|
||||
|
|
|
@ -25,9 +25,7 @@ Style/OpenStructUse:
|
|||
- spec/graphql/mutations/commits/create_spec.rb
|
||||
- spec/helpers/application_settings_helper_spec.rb
|
||||
- spec/helpers/profiles_helper_spec.rb
|
||||
- spec/initializers/doorkeeper_spec.rb
|
||||
- spec/lib/gitlab/auth/o_auth/provider_spec.rb
|
||||
- spec/lib/gitlab/database/migrations/runner_spec.rb
|
||||
- spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb
|
||||
- spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
|
||||
- spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
|
||||
|
|
|
@ -1 +1 @@
|
|||
e02b0d67e48ed5a4493b073c9836d376a780f34d
|
||||
7055518ce76486791c3450a8a47b673891b6e2d6
|
||||
|
|
8
Gemfile
8
Gemfile
|
@ -313,7 +313,7 @@ gem 'pg_query', '~> 2.1'
|
|||
gem 'premailer-rails', '~> 1.10.3'
|
||||
|
||||
# LabKit: Tracing and Correlation
|
||||
gem 'gitlab-labkit', '~> 0.21.1'
|
||||
gem 'gitlab-labkit', '~> 0.21.3'
|
||||
# Thrift is a dependency of gitlab-labkit, we want a version higher than 0.14.0
|
||||
# because of https://gitlab.com/gitlab-org/gitlab/-/issues/321900
|
||||
gem 'thrift', '>= 0.14.0'
|
||||
|
@ -485,14 +485,14 @@ end
|
|||
gem 'spamcheck', '~> 0.1.0'
|
||||
|
||||
# Gitaly GRPC protocol definitions
|
||||
gem 'gitaly', '~> 14.4.0.pre.rc43'
|
||||
gem 'gitaly', '~> 14.6.0.pre.rc1'
|
||||
|
||||
# KAS GRPC protocol definitions
|
||||
gem 'kas-grpc', '~> 0.0.2'
|
||||
|
||||
gem 'grpc', '~> 1.30.2'
|
||||
gem 'grpc', '~> 1.42.0'
|
||||
|
||||
gem 'google-protobuf', '~> 3.17.1'
|
||||
gem 'google-protobuf', '~> 3.19.0'
|
||||
|
||||
gem 'toml-rb', '~> 2.0'
|
||||
|
||||
|
|
22
Gemfile.lock
22
Gemfile.lock
|
@ -452,7 +452,7 @@ GEM
|
|||
rails (>= 3.2.0)
|
||||
git (1.7.0)
|
||||
rchardet (~> 1.8)
|
||||
gitaly (14.4.0.pre.rc43)
|
||||
gitaly (14.6.0.pre.rc1)
|
||||
grpc (~> 1.0)
|
||||
github-markup (1.7.0)
|
||||
gitlab (4.16.1)
|
||||
|
@ -474,10 +474,10 @@ GEM
|
|||
fog-json (~> 1.2.0)
|
||||
mime-types
|
||||
ms_rest_azure (~> 0.12.0)
|
||||
gitlab-labkit (0.21.1)
|
||||
gitlab-labkit (0.21.3)
|
||||
actionpack (>= 5.0.0, < 7.0.0)
|
||||
activesupport (>= 5.0.0, < 7.0.0)
|
||||
grpc (~> 1.30.2)
|
||||
grpc (>= 1.37)
|
||||
jaeger-client (~> 1.1)
|
||||
opentracing (~> 0.4)
|
||||
pg_query (~> 2.1)
|
||||
|
@ -531,8 +531,8 @@ GEM
|
|||
signet (~> 0.12)
|
||||
google-cloud-env (1.5.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
google-protobuf (3.17.3)
|
||||
googleapis-common-protos-types (1.1.0)
|
||||
google-protobuf (3.19.1)
|
||||
googleapis-common-protos-types (1.3.0)
|
||||
google-protobuf (~> 3.14)
|
||||
googleauth (0.14.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
|
@ -580,8 +580,8 @@ GEM
|
|||
graphql (~> 1.6)
|
||||
html-pipeline (~> 2.8)
|
||||
sass (~> 3.4)
|
||||
grpc (1.30.2)
|
||||
google-protobuf (~> 3.12)
|
||||
grpc (1.42.0)
|
||||
google-protobuf (~> 3.18)
|
||||
googleapis-common-protos-types (~> 1.0)
|
||||
gssapi (1.2.0)
|
||||
ffi (>= 1.0.1)
|
||||
|
@ -1473,13 +1473,13 @@ DEPENDENCIES
|
|||
gettext (~> 3.3)
|
||||
gettext_i18n_rails (~> 1.8.0)
|
||||
gettext_i18n_rails_js (~> 1.3)
|
||||
gitaly (~> 14.4.0.pre.rc43)
|
||||
gitaly (~> 14.6.0.pre.rc1)
|
||||
github-markup (~> 1.7.0)
|
||||
gitlab-chronic (~> 0.10.5)
|
||||
gitlab-dangerfiles (~> 2.6.1)
|
||||
gitlab-experiment (~> 0.6.5)
|
||||
gitlab-fog-azure-rm (~> 1.2.0)
|
||||
gitlab-labkit (~> 0.21.1)
|
||||
gitlab-labkit (~> 0.21.3)
|
||||
gitlab-license (~> 2.0)
|
||||
gitlab-license_finder (~> 6.0)
|
||||
gitlab-mail_room (~> 0.0.9)
|
||||
|
@ -1492,7 +1492,7 @@ DEPENDENCIES
|
|||
gitlab_omniauth-ldap (~> 2.1.1)
|
||||
gon (~> 6.4.0)
|
||||
google-api-client (~> 0.33)
|
||||
google-protobuf (~> 3.17.1)
|
||||
google-protobuf (~> 3.19.0)
|
||||
gpgme (~> 2.0.19)
|
||||
grape (~> 1.5.2)
|
||||
grape-entity (~> 0.10.0)
|
||||
|
@ -1502,7 +1502,7 @@ DEPENDENCIES
|
|||
graphlient (~> 0.4.0)
|
||||
graphql (~> 1.11.10)
|
||||
graphql-docs (~> 1.6.0)
|
||||
grpc (~> 1.30.2)
|
||||
grpc (~> 1.42.0)
|
||||
gssapi
|
||||
guard-rspec
|
||||
haml_lint (~> 0.36.0)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import { GlButton, GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
|
||||
import { JOB_SIDEBAR } from '../../constants';
|
||||
import CommitBlock from '../../components/commit_block.vue';
|
||||
|
@ -25,6 +26,7 @@ export default {
|
|||
GlDropdownItem,
|
||||
TooltipOnTruncate,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
bridgeJob: {
|
||||
type: Object,
|
||||
|
@ -54,7 +56,10 @@ export default {
|
|||
</h4>
|
||||
</tooltip-on-truncate>
|
||||
<!-- TODO: implement retry actions -->
|
||||
<div class="gl-flex-grow-1 gl-flex-shrink-0 gl-text-right">
|
||||
<div
|
||||
v-if="glFeatures.triggerJobRetryAction"
|
||||
class="gl-flex-grow-1 gl-flex-shrink-0 gl-text-right"
|
||||
>
|
||||
<gl-dropdown
|
||||
:text="$options.i18n.retryButton"
|
||||
category="primary"
|
||||
|
|
|
@ -26,6 +26,7 @@ import trackShowInviteMemberLink from '~/sidebar/track_invite_members';
|
|||
import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants';
|
||||
import LabelsSelectWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
|
||||
import { LabelType } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
|
||||
import eventHub from '~/sidebar/event_hub';
|
||||
import Translate from '../vue_shared/translate';
|
||||
import SidebarAssignees from './components/assignees/sidebar_assignees.vue';
|
||||
import CopyEmailToClipboard from './components/copy_email_to_clipboard.vue';
|
||||
|
@ -600,6 +601,12 @@ export function mountSidebar(mediator, store) {
|
|||
mountTimeTrackingComponent();
|
||||
|
||||
mountSeverityComponent();
|
||||
|
||||
if (window.gon?.features?.mrAttentionRequests) {
|
||||
eventHub.$on('removeCurrentUserAttentionRequested', () =>
|
||||
mediator.removeCurrentUserAttentionRequested(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { getSidebarOptions };
|
||||
|
|
|
@ -30,7 +30,7 @@ export default class SidebarMediator {
|
|||
this.store.addAssignee(this.store.currentUser);
|
||||
}
|
||||
|
||||
saveAssignees(field) {
|
||||
async saveAssignees(field) {
|
||||
const selected = this.store.assignees.map((u) => u.id);
|
||||
|
||||
// If there are no ids, that means we have to unassign (which is id = 0)
|
||||
|
@ -38,10 +38,22 @@ export default class SidebarMediator {
|
|||
const assignees = selected.length === 0 ? [0] : selected;
|
||||
const data = { assignee_ids: assignees };
|
||||
|
||||
return this.service.update(field, data);
|
||||
try {
|
||||
const res = await this.service.update(field, data);
|
||||
|
||||
this.store.overwrite('assignees', res.data.assignees);
|
||||
|
||||
if (res.data.reviewers) {
|
||||
this.store.overwrite('reviewers', res.data.reviewers);
|
||||
}
|
||||
|
||||
saveReviewers(field) {
|
||||
return Promise.resolve(res);
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
async saveReviewers(field) {
|
||||
const selected = this.store.reviewers.map((u) => u.id);
|
||||
|
||||
// If there are no ids, that means we have to unassign (which is id = 0)
|
||||
|
@ -49,7 +61,16 @@ export default class SidebarMediator {
|
|||
const reviewers = selected.length === 0 ? [0] : selected;
|
||||
const data = { reviewer_ids: reviewers };
|
||||
|
||||
return this.service.update(field, data);
|
||||
try {
|
||||
const res = await this.service.update(field, data);
|
||||
|
||||
this.store.overwrite('reviewers', res.data.reviewers);
|
||||
this.store.overwrite('assignees', res.data.assignees);
|
||||
|
||||
return Promise.resolve(res);
|
||||
} catch (e) {
|
||||
return Promise.reject();
|
||||
}
|
||||
}
|
||||
|
||||
requestReview({ userId, callback }) {
|
||||
|
@ -63,6 +84,19 @@ export default class SidebarMediator {
|
|||
.catch(() => callback(userId, false));
|
||||
}
|
||||
|
||||
removeCurrentUserAttentionRequested() {
|
||||
const currentUserId = gon.current_user_id;
|
||||
|
||||
const currentUserReviewer = this.store.findReviewer({ id: currentUserId });
|
||||
const currentUserAssignee = this.store.findAssignee({ id: currentUserId });
|
||||
|
||||
if (currentUserReviewer?.attention_requested || currentUserAssignee?.attention_requested) {
|
||||
// Update current users attention_requested state
|
||||
this.store.updateReviewer(currentUserId, 'attention_requested');
|
||||
this.store.updateAssignee(currentUserId, 'attention_requested');
|
||||
}
|
||||
}
|
||||
|
||||
async toggleAttentionRequested(type, { user, callback }) {
|
||||
try {
|
||||
const isReviewer = type === 'reviewer';
|
||||
|
@ -82,15 +116,7 @@ export default class SidebarMediator {
|
|||
const currentUserId = gon.current_user_id;
|
||||
|
||||
if (currentUserId !== user.id) {
|
||||
const currentUserReviewerOrAssignee = isReviewer
|
||||
? this.store.findReviewer({ id: currentUserId })
|
||||
: this.store.findAssignee({ id: currentUserId });
|
||||
|
||||
if (currentUserReviewerOrAssignee?.attention_requested) {
|
||||
// Update current users attention_requested state
|
||||
this.store.updateReviewer(currentUserId, 'attention_requested');
|
||||
this.store.updateAssignee(currentUserId, 'attention_requested');
|
||||
}
|
||||
this.removeCurrentUserAttentionRequested();
|
||||
}
|
||||
|
||||
toast(sprintf(__('Requested attention from @%{username}'), { username: user.username }));
|
||||
|
|
|
@ -98,6 +98,10 @@ export default class SidebarStore {
|
|||
}
|
||||
}
|
||||
|
||||
overwrite(key, newData) {
|
||||
this[key] = newData;
|
||||
}
|
||||
|
||||
findAssignee(findAssignee) {
|
||||
return this.assignees.find(({ id }) => id === findAssignee.id);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { GlButton } from '@gitlab/ui';
|
|||
import createFlash from '~/flash';
|
||||
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
|
||||
import { s__ } from '~/locale';
|
||||
import sidebarEventHub from '~/sidebar/event_hub';
|
||||
import eventHub from '../../event_hub';
|
||||
import approvalsMixin from '../../mixins/approvals';
|
||||
import MrWidgetContainer from '../mr_widget_container.vue';
|
||||
|
@ -172,6 +173,7 @@ export default {
|
|||
this.mr.setApprovals(data);
|
||||
eventHub.$emit('MRWidgetUpdateRequested');
|
||||
eventHub.$emit('ApprovalUpdated');
|
||||
sidebarEventHub.$emit('removeCurrentUserAttentionRequested');
|
||||
this.$emit('updated');
|
||||
})
|
||||
.catch(errFn)
|
||||
|
|
|
@ -41,7 +41,7 @@ class Import::GitlabController < Import::BaseController
|
|||
|
||||
override :importable_repos
|
||||
def importable_repos
|
||||
client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS)
|
||||
client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS).to_a
|
||||
end
|
||||
|
||||
override :incompatible_repos
|
||||
|
|
|
@ -19,6 +19,7 @@ class Projects::JobsController < Projects::ApplicationController
|
|||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:infinitely_collapsible_sections, @project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:trigger_job_retry_action, @project, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
layout 'project'
|
||||
|
|
|
@ -18,4 +18,14 @@ module SshKeysHelper
|
|||
container: 'body'
|
||||
}
|
||||
end
|
||||
|
||||
def ssh_key_allowed_algorithms
|
||||
allowed_algorithms = Gitlab::CurrentSettings.allowed_key_types.flat_map do |ssh_key_type_name|
|
||||
Gitlab::SSHPublicKey.supported_algorithms_for_name(ssh_key_type_name)
|
||||
end
|
||||
|
||||
quoted_allowed_algorithms = allowed_algorithms.map { |name| "'#{name}'" }
|
||||
|
||||
Gitlab::Utils.to_exclusive_sentence(quoted_allowed_algorithms)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1292,6 +1292,12 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def use_variables_builder_definitions?
|
||||
strong_memoize(:use_variables_builder_definitions) do
|
||||
::Feature.enabled?(:ci_use_variables_builder_definitions, project, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_message(severity, content)
|
||||
|
|
|
@ -13,6 +13,8 @@ module Ci
|
|||
track_duration do
|
||||
variables = pipeline.variables_builder.scoped_variables(self, environment: environment, dependencies: dependencies)
|
||||
|
||||
next variables if pipeline.use_variables_builder_definitions?
|
||||
|
||||
variables.concat(project.predefined_variables)
|
||||
variables.concat(pipeline.predefined_variables)
|
||||
variables.concat(runner.predefined_variables) if runnable? && runner
|
||||
|
@ -60,49 +62,27 @@ module Ci
|
|||
end
|
||||
|
||||
def user_variables
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
break variables if user.blank?
|
||||
|
||||
variables.append(key: 'GITLAB_USER_ID', value: user.id.to_s)
|
||||
variables.append(key: 'GITLAB_USER_EMAIL', value: user.email)
|
||||
variables.append(key: 'GITLAB_USER_LOGIN', value: user.username)
|
||||
variables.append(key: 'GITLAB_USER_NAME', value: user.name)
|
||||
end
|
||||
pipeline.variables_builder.user_variables(user)
|
||||
end
|
||||
|
||||
def kubernetes_variables
|
||||
::Gitlab::Ci::Variables::Collection.new.tap do |collection|
|
||||
# Should get merged with the cluster kubeconfig in deployment_variables, see
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/335089
|
||||
template = ::Ci::GenerateKubeconfigService.new(self).execute
|
||||
|
||||
if template.valid?
|
||||
collection.append(key: 'KUBECONFIG', value: template.to_yaml, public: false, file: true)
|
||||
end
|
||||
end
|
||||
pipeline.variables_builder.kubernetes_variables(self)
|
||||
end
|
||||
|
||||
def deployment_variables(environment:)
|
||||
return [] unless environment
|
||||
|
||||
project.deployment_variables(
|
||||
environment: environment,
|
||||
kubernetes_namespace: expanded_kubernetes_namespace
|
||||
)
|
||||
pipeline.variables_builder.deployment_variables(job: self, environment: environment)
|
||||
end
|
||||
|
||||
def secret_instance_variables
|
||||
project.ci_instance_variables_for(ref: git_ref)
|
||||
pipeline.variables_builder.secret_instance_variables(ref: git_ref)
|
||||
end
|
||||
|
||||
def secret_group_variables(environment: expanded_environment_name)
|
||||
return [] unless project.group
|
||||
|
||||
project.group.ci_variables_for(git_ref, project, environment: environment)
|
||||
pipeline.variables_builder.secret_group_variables(environment: environment, ref: git_ref)
|
||||
end
|
||||
|
||||
def secret_project_variables(environment: expanded_environment_name)
|
||||
project.ci_variables_for(ref: git_ref, environment: environment)
|
||||
pipeline.variables_builder.secret_project_variables(environment: environment, ref: git_ref)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -627,14 +627,13 @@ class Group < Namespace
|
|||
end
|
||||
end
|
||||
|
||||
def group_member(user)
|
||||
def member(user)
|
||||
if group_members.loaded?
|
||||
group_members.find { |gm| gm.user_id == user.id }
|
||||
else
|
||||
group_members.find_by(user_id: user)
|
||||
end
|
||||
end
|
||||
alias_method :resource_member, :group_member
|
||||
|
||||
def highest_group_member(user)
|
||||
GroupMember.where(source_id: self_and_ancestors_ids, user_id: user.id).order(:access_level).last
|
||||
|
|
|
@ -22,7 +22,7 @@ module Namespaces
|
|||
unscoped.where(id: root_ids)
|
||||
end
|
||||
|
||||
def self_and_ancestors(include_self: true, hierarchy_order: nil)
|
||||
def self_and_ancestors(include_self: true, upto: nil, hierarchy_order: nil)
|
||||
return super unless use_traversal_ids_for_ancestor_scopes?
|
||||
|
||||
ancestors_cte, base_cte = ancestor_ctes
|
||||
|
@ -35,11 +35,15 @@ module Namespaces
|
|||
.where(namespaces[:id].eq(ancestors_cte.table[:ancestor_id]))
|
||||
.order_by_depth(hierarchy_order)
|
||||
|
||||
if include_self
|
||||
records
|
||||
else
|
||||
records.where(ancestors_cte.table[:base_id].not_eq(ancestors_cte.table[:ancestor_id]))
|
||||
unless include_self
|
||||
records = records.where(ancestors_cte.table[:base_id].not_eq(ancestors_cte.table[:ancestor_id]))
|
||||
end
|
||||
|
||||
if upto
|
||||
records = records.where.not(id: unscoped.where(id: upto).select('unnest(traversal_ids)'))
|
||||
end
|
||||
|
||||
records
|
||||
end
|
||||
|
||||
def self_and_ancestor_ids(include_self: true)
|
||||
|
|
|
@ -17,8 +17,8 @@ module Namespaces
|
|||
.where(namespaces: { parent_id: nil })
|
||||
end
|
||||
|
||||
def self_and_ancestors(include_self: true, hierarchy_order: nil)
|
||||
records = Gitlab::ObjectHierarchy.new(all).base_and_ancestors(hierarchy_order: hierarchy_order)
|
||||
def self_and_ancestors(include_self: true, upto: nil, hierarchy_order: nil)
|
||||
records = Gitlab::ObjectHierarchy.new(all).base_and_ancestors(upto: upto, hierarchy_order: hierarchy_order)
|
||||
|
||||
if include_self
|
||||
records
|
||||
|
|
|
@ -1661,14 +1661,13 @@ class Project < ApplicationRecord
|
|||
attrs
|
||||
end
|
||||
|
||||
def project_member(user)
|
||||
def member(user)
|
||||
if project_members.loaded?
|
||||
project_members.find { |member| member.user_id == user.id }
|
||||
else
|
||||
project_members.find_by(user_id: user)
|
||||
end
|
||||
end
|
||||
alias_method :resource_member, :project_member
|
||||
|
||||
def membership_locked?
|
||||
false
|
||||
|
|
|
@ -1336,7 +1336,7 @@ class User < ApplicationRecord
|
|||
|
||||
def can_leave_project?(project)
|
||||
project.namespace != namespace &&
|
||||
project.project_member(self)
|
||||
project.member(self)
|
||||
end
|
||||
|
||||
def full_website_url
|
||||
|
|
|
@ -5,5 +5,9 @@ class MergeRequestSidebarBasicEntity < IssuableSidebarBasicEntity
|
|||
expose :can_merge do |merge_request|
|
||||
merge_request.can_be_merged_by?(current_user)
|
||||
end
|
||||
|
||||
expose :can_update_merge_request do |merge_request|
|
||||
current_user.can?(:update_merge_request, merge_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,8 +59,10 @@ module MergeRequests
|
|||
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
|
||||
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
|
||||
|
||||
unless new_reviewers.include?(current_user)
|
||||
remove_attention_requested(merge_request, current_user)
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup_environments(merge_request)
|
||||
Environments::StopService.new(merge_request.source_project, current_user)
|
||||
|
|
|
@ -23,8 +23,10 @@ module MergeRequests
|
|||
|
||||
execute_assignees_hooks(merge_request, old_assignees) if options[:execute_hooks]
|
||||
|
||||
unless new_assignees.include?(current_user)
|
||||
remove_attention_requested(merge_request, current_user)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ module MergeRequests
|
|||
reset_approvals_cache(merge_request)
|
||||
create_note(merge_request)
|
||||
merge_request_activity_counter.track_unapprove_mr_action(user: current_user)
|
||||
remove_attention_requested(merge_request, current_user)
|
||||
end
|
||||
|
||||
success
|
||||
|
|
|
@ -43,13 +43,9 @@ module ResourceAccessTokens
|
|||
|
||||
def find_member
|
||||
strong_memoize(:member) do
|
||||
if resource.is_a?(Project)
|
||||
resource.project_member(bot_user)
|
||||
elsif resource.is_a?(Group)
|
||||
resource.group_member(bot_user)
|
||||
else
|
||||
false
|
||||
end
|
||||
next false unless resource.is_a?(Project) || resource.is_a?(Group)
|
||||
|
||||
resource.member(bot_user)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
- return unless registration_features_can_be_prompted?
|
||||
|
||||
.form-group
|
||||
= f.label :disabled_ip_restriction_ranges, class: 'label-bold' do
|
||||
= _('Allow access to the following IP addresses')
|
||||
= f.text_field :disabled_ip_restriction_ranges, value: '', class: 'form-control', disabled: true
|
||||
%span.form-text.text-muted
|
||||
= render 'shared/registration_features_discovery_message'
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
= render 'groups/settings/project_access_token_creation', f: f, group: @group
|
||||
= render_if_exists 'groups/settings/delayed_project_removal', f: f, group: @group
|
||||
= render 'groups/settings/ip_restriction_registration_features_cta', f: f
|
||||
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
|
||||
= render_if_exists 'groups/settings/allowed_email_domain', f: f, group: @group
|
||||
= render 'groups/settings/lfs', f: f
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
= _('Add a GPG key')
|
||||
%p.profile-settings-content
|
||||
- help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/repository/gpg_signed_commits/index.md') }
|
||||
= _('Before you can add a GPG key you need to %{help_link_start}Generate it.%{help_link_end}'.html_safe) % {help_link_start: help_link_start, help_link_end: '</a>'.html_safe }
|
||||
= _('Add a GPG key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}').html_safe % {help_link_start: help_link_start, help_link_end: '</a>'.html_safe }
|
||||
= render 'form'
|
||||
%hr
|
||||
%h5
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
.form-group
|
||||
= f.label :key, s_('Profiles|Key'), class: 'label-bold'
|
||||
%p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_ed25519.pub' or '~/.ssh/id_rsa.pub' and begins with 'ssh-ed25519' or 'ssh-rsa'. Do not paste your private SSH key, as that can compromise your identity.")
|
||||
= f.text_area :key, class: "form-control gl-form-input js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-ed25519 …" or "ssh-rsa …"')
|
||||
= f.text_area :key, class: "form-control gl-form-input js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true
|
||||
%p.form-text.text-muted= s_('Profiles|Begins with %{ssh_key_algorithms}.') % { ssh_key_algorithms: ssh_key_allowed_algorithms }
|
||||
.form-row
|
||||
.col.form-group
|
||||
= f.label :title, _('Title'), class: 'label-bold'
|
||||
|
|
|
@ -11,11 +11,8 @@
|
|||
%h5.gl-mt-0
|
||||
= _('Add an SSH key')
|
||||
%p.profile-settings-content
|
||||
- generate_link_url = help_page_path("ssh/index", anchor: 'generate-an-ssh-key-pair')
|
||||
- existing_link_url = help_page_path("ssh/index", anchor: 'see-if-you-have-an-existing-ssh-key-pair')
|
||||
- generate_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: generate_link_url }
|
||||
- existing_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: existing_link_url }
|
||||
= _('To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}.').html_safe % { generate_link_start: generate_link_start, existing_link_start: existing_link_start, link_end: '</a>'.html_safe }
|
||||
- help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('ssh/index.md') }
|
||||
= _('Add an SSH key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}').html_safe % {help_link_start: help_link_start, help_link_end: '</a>'.html_safe }
|
||||
= render 'form'
|
||||
%hr
|
||||
%h5
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
- else
|
||||
%span.token-never-expires-label= _('Never')
|
||||
- if project
|
||||
%td= project.project_member(token.user).human_access
|
||||
%td= project.member(token.user).human_access
|
||||
%td= link_to _('Revoke'), revoke_route_helper.call(token), method: :put, class: "gl-button btn btn-danger btn-sm float-right qa-revoke-button #{'btn-danger-secondary' unless token.expires?}", data: { confirm: _('Are you sure you want to revoke this %{type}? This action cannot be undone.') % { type: type } }
|
||||
- else
|
||||
.settings-message.text-center
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_skip_require_credit_card_for_addon_ci_minutes
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77829
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349841
|
||||
milestone: '14.7'
|
||||
type: development
|
||||
group: group::fulfillment
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_use_variables_builder_definitions
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75254
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349049
|
||||
milestone: '14.7'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: trigger_job_retry_action
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77951
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349966
|
||||
milestone: '14.7'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -2941,6 +2941,27 @@ Input type: `IssueSetEpicInput`
|
|||
| <a id="mutationissuesetepicerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationissuesetepicissue"></a>`issue` | [`Issue`](#issue) | Issue after mutation. |
|
||||
|
||||
### `Mutation.issueSetEscalationPolicy`
|
||||
|
||||
Input type: `IssueSetEscalationPolicyInput`
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationissuesetescalationpolicyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationissuesetescalationpolicyescalationpolicyid"></a>`escalationPolicyId` | [`IncidentManagementEscalationPolicyID`](#incidentmanagementescalationpolicyid) | Global ID of the escalation policy to assign to the issue. Policy will be removed if absent or set to null. |
|
||||
| <a id="mutationissuesetescalationpolicyiid"></a>`iid` | [`String!`](#string) | IID of the issue to mutate. |
|
||||
| <a id="mutationissuesetescalationpolicyprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the issue to mutate is in. |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationissuesetescalationpolicyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationissuesetescalationpolicyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationissuesetescalationpolicyissue"></a>`issue` | [`Issue`](#issue) | Issue after mutation. |
|
||||
|
||||
### `Mutation.issueSetIteration`
|
||||
|
||||
Input type: `IssueSetIterationInput`
|
||||
|
@ -10294,6 +10315,7 @@ Relationship between an epic and an issue.
|
|||
| <a id="epicissueemailsdisabled"></a>`emailsDisabled` | [`Boolean!`](#boolean) | Indicates if a project has email notifications disabled: `true` if email notifications are disabled. |
|
||||
| <a id="epicissueepic"></a>`epic` | [`Epic`](#epic) | Epic to which this issue belongs. |
|
||||
| <a id="epicissueepicissueid"></a>`epicIssueId` | [`ID!`](#id) | ID of the epic-issue relation. |
|
||||
| <a id="epicissueescalationpolicy"></a>`escalationPolicy` | [`EscalationPolicyType`](#escalationpolicytype) | Escalation policy associated with the issue. Available for issues which support escalation. |
|
||||
| <a id="epicissuehealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Current health status. |
|
||||
| <a id="epicissuehidden"></a>`hidden` | [`Boolean`](#boolean) | Indicates the issue is hidden because the author has been banned. Will always return `null` if `ban_user_feature_flag` feature flag is disabled. |
|
||||
| <a id="epicissuehumantimeestimate"></a>`humanTimeEstimate` | [`String`](#string) | Human-readable time estimate of the issue. |
|
||||
|
@ -11476,6 +11498,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
|
|||
| <a id="issueduedate"></a>`dueDate` | [`Time`](#time) | Due date of the issue. |
|
||||
| <a id="issueemailsdisabled"></a>`emailsDisabled` | [`Boolean!`](#boolean) | Indicates if a project has email notifications disabled: `true` if email notifications are disabled. |
|
||||
| <a id="issueepic"></a>`epic` | [`Epic`](#epic) | Epic to which this issue belongs. |
|
||||
| <a id="issueescalationpolicy"></a>`escalationPolicy` | [`EscalationPolicyType`](#escalationpolicytype) | Escalation policy associated with the issue. Available for issues which support escalation. |
|
||||
| <a id="issuehealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Current health status. |
|
||||
| <a id="issuehidden"></a>`hidden` | [`Boolean`](#boolean) | Indicates the issue is hidden because the author has been banned. Will always return `null` if `ban_user_feature_flag` feature flag is disabled. |
|
||||
| <a id="issuehumantimeestimate"></a>`humanTimeEstimate` | [`String`](#string) | Human-readable time estimate of the issue. |
|
||||
|
|
|
@ -8,7 +8,7 @@ module API
|
|||
if options[:project_members]
|
||||
options[:project_members].find { |member| member.source_id == project.id }
|
||||
else
|
||||
project.project_member(options[:current_user])
|
||||
project.member(options[:current_user])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ module API
|
|||
module Entities
|
||||
class ResourceAccessToken < Entities::PersonalAccessToken
|
||||
expose :access_level do |token, options|
|
||||
options[:resource].resource_member(token.user).access_level
|
||||
options[:resource].member(token.user).access_level
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,12 +13,76 @@ module Gitlab
|
|||
def scoped_variables(job, environment:, dependencies:)
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
variables.concat(predefined_variables(job))
|
||||
|
||||
next variables unless pipeline.use_variables_builder_definitions?
|
||||
|
||||
variables.concat(project.predefined_variables)
|
||||
variables.concat(pipeline.predefined_variables)
|
||||
variables.concat(job.runner.predefined_variables) if job.runnable? && job.runner
|
||||
variables.concat(kubernetes_variables(job))
|
||||
variables.concat(deployment_variables(environment: environment, job: job))
|
||||
variables.concat(job.yaml_variables)
|
||||
variables.concat(user_variables(job.user))
|
||||
variables.concat(job.dependency_variables) if dependencies
|
||||
variables.concat(secret_instance_variables(ref: job.git_ref))
|
||||
variables.concat(secret_group_variables(environment: environment, ref: job.git_ref))
|
||||
variables.concat(secret_project_variables(environment: environment, ref: job.git_ref))
|
||||
variables.concat(job.trigger_request.user_variables) if job.trigger_request
|
||||
variables.concat(pipeline.variables)
|
||||
variables.concat(pipeline.pipeline_schedule.job_variables) if pipeline.pipeline_schedule
|
||||
end
|
||||
end
|
||||
|
||||
def kubernetes_variables(job)
|
||||
::Gitlab::Ci::Variables::Collection.new.tap do |collection|
|
||||
# Should get merged with the cluster kubeconfig in deployment_variables, see
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/335089
|
||||
template = ::Ci::GenerateKubeconfigService.new(job).execute
|
||||
|
||||
if template.valid?
|
||||
collection.append(key: 'KUBECONFIG', value: template.to_yaml, public: false, file: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def deployment_variables(environment:, job:)
|
||||
return [] unless environment
|
||||
|
||||
project.deployment_variables(
|
||||
environment: environment,
|
||||
kubernetes_namespace: job.expanded_kubernetes_namespace
|
||||
)
|
||||
end
|
||||
|
||||
def user_variables(user)
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
break variables if user.blank?
|
||||
|
||||
variables.append(key: 'GITLAB_USER_ID', value: user.id.to_s)
|
||||
variables.append(key: 'GITLAB_USER_EMAIL', value: user.email)
|
||||
variables.append(key: 'GITLAB_USER_LOGIN', value: user.username)
|
||||
variables.append(key: 'GITLAB_USER_NAME', value: user.name)
|
||||
end
|
||||
end
|
||||
|
||||
def secret_instance_variables(ref:)
|
||||
project.ci_instance_variables_for(ref: ref)
|
||||
end
|
||||
|
||||
def secret_group_variables(environment:, ref:)
|
||||
return [] unless project.group
|
||||
|
||||
project.group.ci_variables_for(ref, project, environment: environment)
|
||||
end
|
||||
|
||||
def secret_project_variables(environment:, ref:)
|
||||
project.ci_variables_for(ref: ref, environment: environment)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :pipeline
|
||||
delegate :project, to: :pipeline
|
||||
|
||||
def predefined_variables(job)
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
|
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
module Gitlab
|
||||
class SSHPublicKey
|
||||
Technology = Struct.new(:name, :key_class, :supported_sizes)
|
||||
Technology = Struct.new(:name, :key_class, :supported_sizes, :supported_algorithms)
|
||||
|
||||
# See https://man.openbsd.org/sshd#AUTHORIZED_KEYS_FILE_FORMAT for the list of
|
||||
# supported algorithms.
|
||||
TECHNOLOGIES = [
|
||||
Technology.new(:rsa, OpenSSL::PKey::RSA, [1024, 2048, 3072, 4096]),
|
||||
Technology.new(:dsa, OpenSSL::PKey::DSA, [1024, 2048, 3072]),
|
||||
Technology.new(:ecdsa, OpenSSL::PKey::EC, [256, 384, 521]),
|
||||
Technology.new(:ed25519, Net::SSH::Authentication::ED25519::PubKey, [256])
|
||||
Technology.new(:rsa, OpenSSL::PKey::RSA, [1024, 2048, 3072, 4096], %w(ssh-rsa)),
|
||||
Technology.new(:dsa, OpenSSL::PKey::DSA, [1024, 2048, 3072], %w(ssh-dss)),
|
||||
Technology.new(:ecdsa, OpenSSL::PKey::EC, [256, 384, 521], %w(ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521)),
|
||||
Technology.new(:ed25519, Net::SSH::Authentication::ED25519::PubKey, [256], %w(ssh-ed25519))
|
||||
].freeze
|
||||
|
||||
def self.technology(name)
|
||||
|
@ -24,7 +26,15 @@ module Gitlab
|
|||
end
|
||||
|
||||
def self.supported_sizes(name)
|
||||
technology(name)&.supported_sizes
|
||||
technology(name).supported_sizes
|
||||
end
|
||||
|
||||
def self.supported_algorithms
|
||||
TECHNOLOGIES.flat_map { |tech| tech.supported_algorithms }
|
||||
end
|
||||
|
||||
def self.supported_algorithms_for_name(name)
|
||||
technology(name).supported_algorithms
|
||||
end
|
||||
|
||||
def self.sanitize(key_content)
|
||||
|
|
|
@ -179,7 +179,7 @@ namespace :gitlab do
|
|||
task reindex: :environment do
|
||||
unless Gitlab::Database::Reindexing.enabled?
|
||||
puts "This feature (database_reindexing) is currently disabled.".color(:yellow)
|
||||
next
|
||||
exit
|
||||
end
|
||||
|
||||
Gitlab::Database::Reindexing.invoke
|
||||
|
@ -193,7 +193,7 @@ namespace :gitlab do
|
|||
task database_name => :environment do
|
||||
unless Gitlab::Database::Reindexing.enabled?
|
||||
puts "This feature (database_reindexing) is currently disabled.".color(:yellow)
|
||||
next
|
||||
exit
|
||||
end
|
||||
|
||||
Gitlab::Database::Reindexing.invoke(database_name)
|
||||
|
|
|
@ -2009,6 +2009,9 @@ msgstr ""
|
|||
msgid "Add a GPG key"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add a GPG key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add a Jaeger URL to replace this page with a link to your Jaeger server. You first need to %{link_start_tag}install Jaeger%{link_end_tag}."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2075,6 +2078,9 @@ msgstr ""
|
|||
msgid "Add an SSH key"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add an SSH key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add an existing issue"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4620,6 +4626,9 @@ msgstr ""
|
|||
msgid "Are you sure you want to %{action} %{name}?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure you want to approve %{user}?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure you want to attempt to merge?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5535,9 +5544,15 @@ msgstr ""
|
|||
msgid "Billings|Your account has been validated"
|
||||
msgstr ""
|
||||
|
||||
msgid "Billing|%{user} was successfully approved"
|
||||
msgstr ""
|
||||
|
||||
msgid "Billing|An email address is only visible for users with public emails."
|
||||
msgstr ""
|
||||
|
||||
msgid "Billing|An error occurred while approving %{user}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Billing|An error occurred while getting a billable member details"
|
||||
msgstr ""
|
||||
|
||||
|
@ -9070,6 +9085,9 @@ msgstr ""
|
|||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
msgid "Confirm approval"
|
||||
msgstr ""
|
||||
|
||||
msgid "Confirm new password"
|
||||
msgstr ""
|
||||
|
||||
|
@ -25702,9 +25720,6 @@ msgstr ""
|
|||
msgid "Paste this DSN into your Sentry SDK"
|
||||
msgstr ""
|
||||
|
||||
msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_ed25519.pub' or '~/.ssh/id_rsa.pub' and begins with 'ssh-ed25519' or 'ssh-rsa'. Do not paste your private SSH key, as that can compromise your identity."
|
||||
msgstr ""
|
||||
|
||||
msgid "Patch to apply"
|
||||
msgstr ""
|
||||
|
||||
|
@ -27109,6 +27124,9 @@ msgstr ""
|
|||
msgid "Profiles|Avatar will be removed. Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Begins with %{ssh_key_algorithms}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Bio"
|
||||
msgstr ""
|
||||
|
||||
|
@ -27358,9 +27376,6 @@ msgstr ""
|
|||
msgid "Profiles|Type your %{confirmationValue} to confirm:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Typically starts with \"ssh-ed25519 …\" or \"ssh-rsa …\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Update profile settings"
|
||||
msgstr ""
|
||||
|
||||
|
@ -37077,9 +37092,6 @@ msgstr ""
|
|||
msgid "To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "To add the entry manually, provide the following details to the application on your phone."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -30,6 +30,14 @@ RSpec.describe Import::GitlabController do
|
|||
expect(session[:gitlab_access_token]).to eq(token)
|
||||
expect(controller).to redirect_to(status_import_gitlab_url)
|
||||
end
|
||||
|
||||
it "importable_repos should return an array" do
|
||||
allow_next_instance_of(Gitlab::GitlabImport::Client) do |instance|
|
||||
allow(instance).to receive(:projects).and_return([{ "id": 1 }].to_enum)
|
||||
end
|
||||
|
||||
expect(controller.send(:importable_repos)).to be_an_instance_of(Array)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET status" do
|
||||
|
|
|
@ -7,12 +7,16 @@ import { mockCommit, mockJob } from '../mock_data';
|
|||
describe('Bridge Sidebar', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (props) => {
|
||||
const createComponent = ({ featureFlag } = {}) => {
|
||||
wrapper = shallowMount(BridgeSidebar, {
|
||||
provide: {
|
||||
glFeatures: {
|
||||
triggerJobRetryAction: featureFlag,
|
||||
},
|
||||
},
|
||||
propsData: {
|
||||
bridgeJob: mockJob,
|
||||
commit: mockCommit,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -35,10 +39,6 @@ describe('Bridge Sidebar', () => {
|
|||
expect(findJobTitle().text()).toBe(mockJob.name);
|
||||
});
|
||||
|
||||
it('renders retry dropdown', () => {
|
||||
expect(findRetryDropdown().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders commit information', () => {
|
||||
expect(findCommitBlock().exists()).toBe(true);
|
||||
});
|
||||
|
@ -57,4 +57,24 @@ describe('Bridge Sidebar', () => {
|
|||
expect(wrapper.emitted('toggleSidebar')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('retry action', () => {
|
||||
describe('when feature flag is ON', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ featureFlag: true });
|
||||
});
|
||||
|
||||
it('renders retry dropdown', () => {
|
||||
expect(findRetryDropdown().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when feature flag is OFF', () => {
|
||||
it('does not render retry dropdown', () => {
|
||||
createComponent({ featureFlag: false });
|
||||
|
||||
expect(findRetryDropdown().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe SshKeysHelper do
|
||||
describe '#ssh_key_allowed_algorithms' do
|
||||
it 'returns string with the names of allowed algorithms that are quoted and joined by commas' do
|
||||
allowed_algorithms = Gitlab::CurrentSettings.allowed_key_types.flat_map do |ssh_key_type_name|
|
||||
Gitlab::SSHPublicKey.supported_algorithms_for_name(ssh_key_type_name)
|
||||
end
|
||||
|
||||
quoted_allowed_algorithms = allowed_algorithms.map { |name| "'#{name}'" }
|
||||
|
||||
expected_string = Gitlab::Utils.to_exclusive_sentence(quoted_allowed_algorithms)
|
||||
|
||||
expect(ssh_key_allowed_algorithms).to eq(expected_string)
|
||||
end
|
||||
|
||||
it 'returns only allowed algorithms' do
|
||||
expect(ssh_key_allowed_algorithms).to match('ed25519')
|
||||
stub_application_setting(ed25519_key_restriction: ApplicationSetting::FORBIDDEN_KEY_VALUE)
|
||||
expect(ssh_key_allowed_algorithms).not_to match('ed25519')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -24,7 +24,7 @@ RSpec.describe Doorkeeper.configuration do
|
|||
before do
|
||||
allow(controller).to receive(:current_user).and_return(current_user)
|
||||
allow(controller).to receive(:session).and_return({})
|
||||
allow(controller).to receive(:request).and_return(OpenStruct.new(fullpath: '/return-path'))
|
||||
allow(controller).to receive(:request).and_return(double('request', fullpath: '/return-path'))
|
||||
allow(controller).to receive(:redirect_to)
|
||||
allow(controller).to receive(:new_user_session_url).and_return('/login')
|
||||
end
|
||||
|
|
|
@ -3,25 +3,201 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Variables::Builder do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
let_it_be(:user) { project.owner }
|
||||
let_it_be(:job) do
|
||||
create(:ci_build,
|
||||
pipeline: pipeline,
|
||||
user: user,
|
||||
yaml_variables: [{ key: 'YAML_VARIABLE', value: 'value' }]
|
||||
)
|
||||
end
|
||||
|
||||
let(:builder) { described_class.new(pipeline) }
|
||||
let(:pipeline) { create(:ci_pipeline) }
|
||||
let(:job) { create(:ci_build, pipeline: pipeline) }
|
||||
|
||||
describe '#scoped_variables' do
|
||||
let(:environment) { job.expanded_environment_name }
|
||||
let(:dependencies) { true }
|
||||
let(:predefined_variables) do
|
||||
[
|
||||
{ key: 'CI_JOB_NAME',
|
||||
value: job.name },
|
||||
{ key: 'CI_JOB_STAGE',
|
||||
value: job.stage },
|
||||
{ key: 'CI_NODE_TOTAL',
|
||||
value: '1' },
|
||||
{ key: 'CI_BUILD_NAME',
|
||||
value: job.name },
|
||||
{ key: 'CI_BUILD_STAGE',
|
||||
value: job.stage },
|
||||
{ key: 'CI',
|
||||
value: 'true' },
|
||||
{ key: 'GITLAB_CI',
|
||||
value: 'true' },
|
||||
{ key: 'CI_SERVER_URL',
|
||||
value: Gitlab.config.gitlab.url },
|
||||
{ key: 'CI_SERVER_HOST',
|
||||
value: Gitlab.config.gitlab.host },
|
||||
{ key: 'CI_SERVER_PORT',
|
||||
value: Gitlab.config.gitlab.port.to_s },
|
||||
{ key: 'CI_SERVER_PROTOCOL',
|
||||
value: Gitlab.config.gitlab.protocol },
|
||||
{ key: 'CI_SERVER_NAME',
|
||||
value: 'GitLab' },
|
||||
{ key: 'CI_SERVER_VERSION',
|
||||
value: Gitlab::VERSION },
|
||||
{ key: 'CI_SERVER_VERSION_MAJOR',
|
||||
value: Gitlab.version_info.major.to_s },
|
||||
{ key: 'CI_SERVER_VERSION_MINOR',
|
||||
value: Gitlab.version_info.minor.to_s },
|
||||
{ key: 'CI_SERVER_VERSION_PATCH',
|
||||
value: Gitlab.version_info.patch.to_s },
|
||||
{ key: 'CI_SERVER_REVISION',
|
||||
value: Gitlab.revision },
|
||||
{ key: 'GITLAB_FEATURES',
|
||||
value: project.licensed_features.join(',') },
|
||||
{ key: 'CI_PROJECT_ID',
|
||||
value: project.id.to_s },
|
||||
{ key: 'CI_PROJECT_NAME',
|
||||
value: project.path },
|
||||
{ key: 'CI_PROJECT_TITLE',
|
||||
value: project.title },
|
||||
{ key: 'CI_PROJECT_PATH',
|
||||
value: project.full_path },
|
||||
{ key: 'CI_PROJECT_PATH_SLUG',
|
||||
value: project.full_path_slug },
|
||||
{ key: 'CI_PROJECT_NAMESPACE',
|
||||
value: project.namespace.full_path },
|
||||
{ key: 'CI_PROJECT_ROOT_NAMESPACE',
|
||||
value: project.namespace.root_ancestor.path },
|
||||
{ key: 'CI_PROJECT_URL',
|
||||
value: project.web_url },
|
||||
{ key: 'CI_PROJECT_VISIBILITY',
|
||||
value: "private" },
|
||||
{ key: 'CI_PROJECT_REPOSITORY_LANGUAGES',
|
||||
value: project.repository_languages.map(&:name).join(',').downcase },
|
||||
{ key: 'CI_PROJECT_CLASSIFICATION_LABEL',
|
||||
value: project.external_authorization_classification_label },
|
||||
{ key: 'CI_DEFAULT_BRANCH',
|
||||
value: project.default_branch },
|
||||
{ key: 'CI_CONFIG_PATH',
|
||||
value: project.ci_config_path_or_default },
|
||||
{ key: 'CI_PAGES_DOMAIN',
|
||||
value: Gitlab.config.pages.host },
|
||||
{ key: 'CI_PAGES_URL',
|
||||
value: project.pages_url },
|
||||
{ key: 'CI_API_V4_URL',
|
||||
value: API::Helpers::Version.new('v4').root_url },
|
||||
{ key: 'CI_PIPELINE_IID',
|
||||
value: pipeline.iid.to_s },
|
||||
{ key: 'CI_PIPELINE_SOURCE',
|
||||
value: pipeline.source },
|
||||
{ key: 'CI_PIPELINE_CREATED_AT',
|
||||
value: pipeline.created_at.iso8601 },
|
||||
{ key: 'CI_COMMIT_SHA',
|
||||
value: job.sha },
|
||||
{ key: 'CI_COMMIT_SHORT_SHA',
|
||||
value: job.short_sha },
|
||||
{ key: 'CI_COMMIT_BEFORE_SHA',
|
||||
value: job.before_sha },
|
||||
{ key: 'CI_COMMIT_REF_NAME',
|
||||
value: job.ref },
|
||||
{ key: 'CI_COMMIT_REF_SLUG',
|
||||
value: job.ref_slug },
|
||||
{ key: 'CI_COMMIT_BRANCH',
|
||||
value: job.ref },
|
||||
{ key: 'CI_COMMIT_MESSAGE',
|
||||
value: pipeline.git_commit_message },
|
||||
{ key: 'CI_COMMIT_TITLE',
|
||||
value: pipeline.git_commit_title },
|
||||
{ key: 'CI_COMMIT_DESCRIPTION',
|
||||
value: pipeline.git_commit_description },
|
||||
{ key: 'CI_COMMIT_REF_PROTECTED',
|
||||
value: (!!pipeline.protected_ref?).to_s },
|
||||
{ key: 'CI_COMMIT_TIMESTAMP',
|
||||
value: pipeline.git_commit_timestamp },
|
||||
{ key: 'CI_COMMIT_AUTHOR',
|
||||
value: pipeline.git_author_full_text },
|
||||
{ key: 'CI_BUILD_REF',
|
||||
value: job.sha },
|
||||
{ key: 'CI_BUILD_BEFORE_SHA',
|
||||
value: job.before_sha },
|
||||
{ key: 'CI_BUILD_REF_NAME',
|
||||
value: job.ref },
|
||||
{ key: 'CI_BUILD_REF_SLUG',
|
||||
value: job.ref_slug },
|
||||
{ key: 'YAML_VARIABLE',
|
||||
value: 'value' },
|
||||
{ key: 'GITLAB_USER_ID',
|
||||
value: user.id.to_s },
|
||||
{ key: 'GITLAB_USER_EMAIL',
|
||||
value: user.email },
|
||||
{ key: 'GITLAB_USER_LOGIN',
|
||||
value: user.username },
|
||||
{ key: 'GITLAB_USER_NAME',
|
||||
value: user.name }
|
||||
].map { |var| var.merge(public: true, masked: false) }
|
||||
end
|
||||
|
||||
subject { builder.scoped_variables(job, environment: environment, dependencies: dependencies) }
|
||||
|
||||
it 'returns the expected variables' do
|
||||
keys = %w[CI_JOB_NAME
|
||||
CI_JOB_STAGE
|
||||
CI_NODE_TOTAL
|
||||
CI_BUILD_NAME
|
||||
CI_BUILD_STAGE]
|
||||
it { is_expected.to be_instance_of(Gitlab::Ci::Variables::Collection) }
|
||||
|
||||
subject.map { |env| env[:key] }.tap do |names|
|
||||
expect(names).to include(*keys)
|
||||
it { expect(subject.to_runner_variables).to eq(predefined_variables) }
|
||||
|
||||
context 'variables ordering' do
|
||||
def var(name, value)
|
||||
{ key: name, value: value.to_s, public: true, masked: false }
|
||||
end
|
||||
|
||||
before do
|
||||
allow(builder).to receive(:predefined_variables) { [var('A', 1), var('B', 1)] }
|
||||
allow(project).to receive(:predefined_variables) { [var('B', 2), var('C', 2)] }
|
||||
allow(pipeline).to receive(:predefined_variables) { [var('C', 3), var('D', 3)] }
|
||||
allow(job).to receive(:runner) { double(predefined_variables: [var('D', 4), var('E', 4)]) }
|
||||
allow(builder).to receive(:kubernetes_variables) { [var('E', 5), var('F', 5)] }
|
||||
allow(builder).to receive(:deployment_variables) { [var('F', 6), var('G', 6)] }
|
||||
allow(job).to receive(:yaml_variables) { [var('G', 7), var('H', 7)] }
|
||||
allow(builder).to receive(:user_variables) { [var('H', 8), var('I', 8)] }
|
||||
allow(job).to receive(:dependency_variables) { [var('I', 9), var('J', 9)] }
|
||||
allow(builder).to receive(:secret_instance_variables) { [var('J', 10), var('K', 10)] }
|
||||
allow(builder).to receive(:secret_group_variables) { [var('K', 11), var('L', 11)] }
|
||||
allow(builder).to receive(:secret_project_variables) { [var('L', 12), var('M', 12)] }
|
||||
allow(job).to receive(:trigger_request) { double(user_variables: [var('M', 13), var('N', 13)]) }
|
||||
allow(pipeline).to receive(:variables) { [var('N', 14), var('O', 14)] }
|
||||
allow(pipeline).to receive(:pipeline_schedule) { double(job_variables: [var('O', 15), var('P', 15)]) }
|
||||
end
|
||||
|
||||
it 'returns variables in order depending on resource hierarchy' do
|
||||
expect(subject.to_runner_variables).to eq(
|
||||
[var('A', 1), var('B', 1),
|
||||
var('B', 2), var('C', 2),
|
||||
var('C', 3), var('D', 3),
|
||||
var('D', 4), var('E', 4),
|
||||
var('E', 5), var('F', 5),
|
||||
var('F', 6), var('G', 6),
|
||||
var('G', 7), var('H', 7),
|
||||
var('H', 8), var('I', 8),
|
||||
var('I', 9), var('J', 9),
|
||||
var('J', 10), var('K', 10),
|
||||
var('K', 11), var('L', 11),
|
||||
var('L', 12), var('M', 12),
|
||||
var('M', 13), var('N', 13),
|
||||
var('N', 14), var('O', 14),
|
||||
var('O', 15), var('P', 15)])
|
||||
end
|
||||
|
||||
it 'overrides duplicate keys depending on resource hierarchy' do
|
||||
expect(subject.to_hash).to match(
|
||||
'A' => '1', 'B' => '2',
|
||||
'C' => '3', 'D' => '4',
|
||||
'E' => '5', 'F' => '6',
|
||||
'G' => '7', 'H' => '8',
|
||||
'I' => '9', 'J' => '10',
|
||||
'K' => '11', 'L' => '12',
|
||||
'M' => '13', 'N' => '14',
|
||||
'O' => '15', 'P' => '15')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,7 +28,7 @@ RSpec.describe Gitlab::Database::Migrations::Runner do
|
|||
allow(ActiveRecord::Migrator).to receive(:new) do |dir, _all_migrations, _schema_migration_class, version_to_migrate|
|
||||
migrator = double(ActiveRecord::Migrator)
|
||||
expect(migrator).to receive(:run) do
|
||||
migration_runs << OpenStruct.new(dir: dir, version_to_migrate: version_to_migrate)
|
||||
migration_runs << double('migrator', dir: dir, version_to_migrate: version_to_migrate)
|
||||
end
|
||||
migrator
|
||||
end
|
||||
|
|
|
@ -39,14 +39,43 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
|
|||
]
|
||||
end
|
||||
|
||||
subject { described_class.supported_sizes(name) }
|
||||
|
||||
with_them do
|
||||
it { expect(described_class.supported_sizes(name)).to eq(sizes) }
|
||||
it { expect(described_class.supported_sizes(name.to_s)).to eq(sizes) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.supported_algorithms' do
|
||||
it 'returns all supported algorithms' do
|
||||
expect(described_class.supported_algorithms).to eq(
|
||||
%w(
|
||||
ssh-rsa
|
||||
ssh-dss
|
||||
ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521
|
||||
ssh-ed25519
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.supported_algorithms_for_name' do
|
||||
where(:name, :algorithms) do
|
||||
[
|
||||
[:rsa, %w(ssh-rsa)],
|
||||
[:dsa, %w(ssh-dss)],
|
||||
[:ecdsa, %w(ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521)],
|
||||
[:ed25519, %w(ssh-ed25519)]
|
||||
]
|
||||
end
|
||||
|
||||
with_them do
|
||||
it "returns all supported algorithms for #{params[:name]}" do
|
||||
expect(described_class.supported_algorithms_for_name(name)).to eq(algorithms)
|
||||
expect(described_class.supported_algorithms_for_name(name.to_s)).to eq(algorithms)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.sanitize(key_content)' do
|
||||
let(:content) { build(:key).key }
|
||||
|
||||
|
|
|
@ -2824,7 +2824,7 @@ RSpec.describe Ci::Build do
|
|||
allow(build).to receive(:dependency_variables) { [job_dependency_var] }
|
||||
allow(build).to receive(:dependency_proxy_variables) { [dependency_proxy_var] }
|
||||
|
||||
allow(build.project)
|
||||
allow(build.pipeline.project)
|
||||
.to receive(:predefined_variables) { [project_pre_var] }
|
||||
|
||||
project.variables.create!(key: 'secret', value: 'value')
|
||||
|
@ -3124,7 +3124,7 @@ RSpec.describe Ci::Build do
|
|||
|
||||
context 'when the branch is protected' do
|
||||
before do
|
||||
allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
|
||||
allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to include(protected_variable) }
|
||||
|
@ -3132,7 +3132,7 @@ RSpec.describe Ci::Build do
|
|||
|
||||
context 'when the tag is protected' do
|
||||
before do
|
||||
allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
|
||||
allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to include(protected_variable) }
|
||||
|
@ -3171,7 +3171,7 @@ RSpec.describe Ci::Build do
|
|||
|
||||
context 'when the branch is protected' do
|
||||
before do
|
||||
allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
|
||||
allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to include(protected_variable) }
|
||||
|
@ -3179,7 +3179,7 @@ RSpec.describe Ci::Build do
|
|||
|
||||
context 'when the tag is protected' do
|
||||
before do
|
||||
allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
|
||||
allow(build.pipeline.project).to receive(:protected_for?).with(ref).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to include(protected_variable) }
|
||||
|
@ -3566,6 +3566,20 @@ RSpec.describe Ci::Build do
|
|||
|
||||
build.scoped_variables
|
||||
end
|
||||
|
||||
context 'when variables builder is used' do
|
||||
it 'returns the same variables' do
|
||||
build.user = create(:user)
|
||||
|
||||
allow(build.pipeline).to receive(:use_variables_builder_definitions?).and_return(false)
|
||||
legacy_variables = build.scoped_variables.to_hash
|
||||
|
||||
allow(build.pipeline).to receive(:use_variables_builder_definitions?).and_return(true)
|
||||
new_variables = build.scoped_variables.to_hash
|
||||
|
||||
expect(new_variables).to eq(legacy_variables)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#simple_variables_without_dependencies' do
|
||||
|
@ -3578,7 +3592,8 @@ RSpec.describe Ci::Build do
|
|||
|
||||
shared_examples "secret CI variables" do
|
||||
context 'when ref is branch' do
|
||||
let(:build) { create(:ci_build, ref: 'master', tag: false, project: project) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
let(:build) { create(:ci_build, ref: 'master', tag: false, pipeline: pipeline, project: project) }
|
||||
|
||||
context 'when ref is protected' do
|
||||
before do
|
||||
|
@ -3594,7 +3609,8 @@ RSpec.describe Ci::Build do
|
|||
end
|
||||
|
||||
context 'when ref is tag' do
|
||||
let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, project: project) }
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, pipeline: pipeline, project: project) }
|
||||
|
||||
context 'when ref is protected' do
|
||||
before do
|
||||
|
@ -3692,8 +3708,6 @@ RSpec.describe Ci::Build do
|
|||
.and_return(project_variables)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(project_variables) }
|
||||
|
||||
context 'environment is nil' do
|
||||
let(:environment) { nil }
|
||||
|
||||
|
@ -3701,6 +3715,35 @@ RSpec.describe Ci::Build do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#user_variables' do
|
||||
subject { build.user_variables.to_hash }
|
||||
|
||||
context 'with user' do
|
||||
let(:expected_variables) do
|
||||
{
|
||||
'GITLAB_USER_EMAIL' => user.email,
|
||||
'GITLAB_USER_ID' => user.id.to_s,
|
||||
'GITLAB_USER_LOGIN' => user.username,
|
||||
'GITLAB_USER_NAME' => user.name
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
build.user = user
|
||||
end
|
||||
|
||||
it { is_expected.to eq(expected_variables) }
|
||||
end
|
||||
|
||||
context 'without user' do
|
||||
before do
|
||||
expect(build).to receive(:user).and_return(nil)
|
||||
end
|
||||
|
||||
it { is_expected.to be_empty }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#any_unmet_prerequisites?' do
|
||||
let(:build) { create(:ci_build, :created) }
|
||||
|
||||
|
|
|
@ -425,7 +425,7 @@ RSpec.describe API::MavenPackages do
|
|||
|
||||
context 'internal project' do
|
||||
before do
|
||||
group.group_member(user).destroy!
|
||||
group.member(user).destroy!
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ RSpec.describe 'merge requests discussions' do
|
|||
|
||||
context 'when current_user role changes' do
|
||||
before do
|
||||
Members::UpdateService.new(owner, access_level: Gitlab::Access::GUEST).execute(project.project_member(user))
|
||||
Members::UpdateService.new(owner, access_level: Gitlab::Access::GUEST).execute(project.member(user))
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
|
|
|
@ -50,7 +50,7 @@ RSpec.shared_examples 'project access tokens available #create' do
|
|||
expect(created_token.name).to eq(access_token_params[:name])
|
||||
expect(created_token.scopes).to eq(access_token_params[:scopes])
|
||||
expect(created_token.expires_at).to eq(access_token_params[:expires_at])
|
||||
expect(project.project_member(created_token.user).access_level).to eq(access_level)
|
||||
expect(project.member(created_token.user).access_level).to eq(access_level)
|
||||
end
|
||||
|
||||
it 'creates project bot user' do
|
||||
|
|
|
@ -233,7 +233,7 @@ RSpec.shared_examples 'snippet visibility' do
|
|||
project.update!(visibility_level: Gitlab::VisibilityLevel.level_value(project_visibility.to_s), snippets_access_level: feature_visibility)
|
||||
|
||||
if user_type == :external
|
||||
member = project.project_member(external)
|
||||
member = project.member(external)
|
||||
|
||||
if project.private?
|
||||
project.add_developer(external) unless member
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
RSpec.shared_examples 'inherited access level as a member of entity' do
|
||||
let(:parent_entity) { create(:group) }
|
||||
let(:user) { create(:user) }
|
||||
let(:member) { entity.is_a?(Group) ? entity.group_member(user) : entity.project_member(user) }
|
||||
let(:member) { entity.member(user) }
|
||||
|
||||
context 'with root parent_entity developer member' do
|
||||
before do
|
||||
|
@ -49,7 +49,7 @@ RSpec.shared_examples 'inherited access level as a member of entity' do
|
|||
|
||||
entity.add_maintainer(non_member_user)
|
||||
|
||||
non_member = entity.is_a?(Group) ? entity.group_member(non_member_user) : entity.project_member(non_member_user)
|
||||
non_member = entity.member(non_member_user)
|
||||
|
||||
expect { non_member.update!(access_level: Gitlab::Access::GUEST) }
|
||||
.to change { non_member.reload.access_level }
|
||||
|
|
|
@ -130,6 +130,12 @@ RSpec.shared_examples 'namespace traversal scopes' do
|
|||
|
||||
it { is_expected.to contain_exactly(group_2, nested_group_2, deep_nested_group_2) }
|
||||
end
|
||||
|
||||
context 'with upto' do
|
||||
subject { described_class.where(id: deep_nested_group_1).self_and_ancestors(upto: nested_group_1.id) }
|
||||
|
||||
it { is_expected.to contain_exactly(deep_nested_group_1) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.self_and_ancestors' do
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'renders registration features prompt' do |disabled_field, feature_title|
|
||||
it 'renders a placeholder input with registration features message' do
|
||||
render
|
||||
|
||||
if disabled_field
|
||||
expect(rendered).to have_field(disabled_field, disabled: true)
|
||||
end
|
||||
|
||||
expect(rendered).to have_content(s_("RegistrationFeatures|Want to %{feature_title} for free?") % { feature_title: feature_title || s_('RegistrationFeatures|use this feature') })
|
||||
expect(rendered).to have_link(s_('RegistrationFeatures|Registration Features Program'))
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'does not render registration features prompt' do |disabled_field, feature_title|
|
||||
it 'does not render a placeholder input with registration features message' do
|
||||
render
|
||||
|
||||
if disabled_field
|
||||
expect(rendered).not_to have_field(disabled_field, disabled: true)
|
||||
end
|
||||
|
||||
expect(rendered).not_to have_content(s_("RegistrationFeatures|Want to %{feature_title} for free?") % { feature_title: feature_title || s_('RegistrationFeatures|use this feature') })
|
||||
expect(rendered).not_to have_link(s_('RegistrationFeatures|Registration Features Program'))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
SystemExitDetected = Class.new(RuntimeError)
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.around do |example|
|
||||
example.run
|
||||
rescue SystemExit
|
||||
# In any cases, we cannot raise SystemExit in the tests,
|
||||
# because it'll skip any following tests from running.
|
||||
# Convert it to something that won't skip everything.
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/350060
|
||||
raise SystemExitDetected, "SystemExit should be rescued in the tests!"
|
||||
end
|
||||
end
|
|
@ -214,7 +214,7 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
|
|||
expect(Gitlab::Database::Reindexing).to receive(:enabled?).and_return(false)
|
||||
expect(Gitlab::Database::Reindexing).not_to receive(:invoke)
|
||||
|
||||
run_rake_task('gitlab:db:reindex')
|
||||
expect { run_rake_task('gitlab:db:reindex') }.to raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -233,7 +233,7 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
|
|||
expect(Gitlab::Database::Reindexing).to receive(:enabled?).and_return(false)
|
||||
expect(Gitlab::Database::Reindexing).not_to receive(:invoke).with(database_name)
|
||||
|
||||
run_rake_task("gitlab:db:reindex:#{database_name}")
|
||||
expect { run_rake_task("gitlab:db:reindex:#{database_name}") }.to raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -115,4 +115,40 @@ RSpec.describe 'groups/edit.html.haml' do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ip_restriction' do
|
||||
let(:group) { create(:group) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
group.add_owner(user)
|
||||
|
||||
assign(:group, group)
|
||||
allow(view).to receive(:current_user) { user }
|
||||
end
|
||||
|
||||
context 'prompt user about registration features' do
|
||||
before do
|
||||
if Gitlab.ee?
|
||||
allow(License).to receive(:current).and_return(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with service ping disabled' do
|
||||
before do
|
||||
stub_application_setting(usage_ping_enabled: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'renders registration features prompt', :group_disabled_ip_restriction_ranges
|
||||
end
|
||||
|
||||
context 'with service ping enabled' do
|
||||
before do
|
||||
stub_application_setting(usage_ping_enabled: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'does not render registration features prompt', :group_disabled_ip_restriction_ranges
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'profiles/keys/_form.html.haml' do
|
||||
include SshKeysHelper
|
||||
|
||||
let_it_be(:key) { Key.new }
|
||||
|
||||
let(:page) { Capybara::Node::Simple.new(rendered) }
|
||||
|
@ -23,8 +25,8 @@ RSpec.describe 'profiles/keys/_form.html.haml' do
|
|||
end
|
||||
|
||||
it 'has the key field', :aggregate_failures do
|
||||
expect(rendered).to have_field('Key', type: 'textarea', placeholder: 'Typically starts with "ssh-ed25519 …" or "ssh-rsa …"')
|
||||
expect(rendered).to have_text("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_ed25519.pub' or '~/.ssh/id_rsa.pub' and begins with 'ssh-ed25519' or 'ssh-rsa'. Do not paste your private SSH key, as that can compromise your identity.")
|
||||
expect(rendered).to have_field('Key', type: 'textarea')
|
||||
expect(rendered).to have_text(s_('Profiles|Begins with %{ssh_key_algorithms}.') % { ssh_key_algorithms: ssh_key_allowed_algorithms })
|
||||
end
|
||||
|
||||
it 'has the title field', :aggregate_failures do
|
||||
|
|
Loading…
Reference in New Issue