Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-05-12 00:12:55 +00:00
parent 9643359dd3
commit 4e65fc3589
92 changed files with 1656 additions and 370 deletions

View File

@ -437,7 +437,6 @@ lib/gitlab/checks/**
/doc/administration/maintenance_mode/ @axil
/doc/administration/merge_request_diffs.md @aqualls
/doc/administration/monitoring/github_imports.md @eread @ashrafkhamis
/doc/administration/monitoring/gitlab_self_monitoring_project/ @msedlakjakubowski
/doc/administration/monitoring/index.md @msedlakjakubowski
/doc/administration/monitoring/ip_allowlist.md @jglassman1
/doc/administration/monitoring/performance/gitlab_configuration.md @msedlakjakubowski
@ -565,6 +564,7 @@ lib/gitlab/checks/**
/doc/api/license.md @fneill
/doc/api/linked_epics.md @msedlakjakubowski
/doc/api/lint.md @marcel.amirault
/doc/api/managed_licenses.md @fneill
/doc/api/markdown.md @msedlakjakubowski
/doc/api/member_roles.md @jglassman1
/doc/api/members.md @jglassman1
@ -652,6 +652,7 @@ lib/gitlab/checks/**
/doc/ci/chatops/ @eread @ashrafkhamis
/doc/ci/cloud_deployment/ @phillipwells
/doc/ci/cloud_services/ @marcel.amirault
/doc/ci/components/ @marcel.amirault
/doc/ci/directed_acyclic_graph/ @marcel.amirault
/doc/ci/docker/using_docker_images.md @fneill
/doc/ci/environments/ @phillipwells
@ -982,6 +983,7 @@ lib/gitlab/checks/**
/doc/user/tasks.md @msedlakjakubowski
/doc/user/todos.md @msedlakjakubowski
/doc/user/usage_quotas.md @fneill
/doc/user/workspace/ @ashrafkhamis
# End rake-managed-docs-block
[Authentication and Authorization] @gitlab-org/manage/authentication-and-authorization/approvers

View File

@ -27,7 +27,7 @@ trigger-omnibus-env-ce:
extends:
- .trigger-omnibus-env-ce
variables:
FOSS_ONLY: "1" # set FOSS_ONLY because we don't pass it via trigger job
FOSS_ONLY: "1" # set FOSS_ONLY because we don't pass it via trigger job
# TODO: enable once ee jobs are added
# trigger-omnibus:

View File

@ -34,7 +34,7 @@ rails-production-server-boot:
- sed --in-place "s:/home/git/gitlab:${PWD}:" config/puma.rb
- echo 'bind "tcp://127.0.0.1:3000"' >> config/puma.rb
- bundle exec puma --environment production --config config/puma.rb &
- sleep 40 # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/114124#note_1309506358
- sleep 40 # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/114124#note_1309506358
- retry_times_sleep 10 5 "curl http://127.0.0.1:3000"
- kill $(jobs -p)

View File

@ -214,9 +214,9 @@ stages:
fi
- |
bundle exec relate-failure-issue \
--input-files "$CI_PROJECT_DIR/gitlab-qa-run-*/**/rspec-*.json" \
--input-files "${CI_PROJECT_DIR}/gitlab-qa-run-*/**/rspec-*.json" \
--project "gitlab-org/gitlab" \
--token "$RELATE_TEST_FAILURE_TOKEN"
--token "${RELATE_TEST_FAILURE_TOKEN}"
.generate-test-session:
extends:
@ -247,7 +247,6 @@ stages:
- .ruby-image
stage: notify
variables:
QA_RSPEC_XML_FILE_PATTERN: $CI_PROJECT_DIR/gitlab-qa-run-*/**/rspec-*.xml
SLACK_ICON_EMOJI: ci_failing
STATUS_SYM: ☠️
STATUS: failed
@ -259,7 +258,7 @@ stages:
echo "Test suite passed. Exiting..."
exit 0
fi
- bundle exec gitlab-qa-report --prepare-stage-reports "$QA_RSPEC_XML_FILE_PATTERN" # generate summary
- bundle exec prepare-stage-reports --input-files "${CI_PROJECT_DIR}/gitlab-qa-run-*/**/rspec-*.xml"
- !reference [.notify-slack-qa, script]
# ==========================================

View File

@ -92,7 +92,7 @@ review-build-cng:
.review-workflow-base:
image: ${REVIEW_APPS_IMAGE}
retry:
max: 2 # This is confusing but this means "3 runs at max"
max: 2 # This is confusing but this means "3 runs at max"
variables:
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"

View File

@ -169,7 +169,7 @@ notify-slack:
TYPE: "(review-app) "
when: on_failure
script:
- bundle exec gitlab-qa-report --prepare-stage-reports "$CI_PROJECT_DIR/qa/tmp/rspec-*.xml" # generate summary
- bundle exec prepare-stage-reports --input-files "${CI_PROJECT_DIR}/qa/tmp/rspec-*.xml"
- !reference [.notify-slack-qa, script]
export-test-metrics:

View File

@ -5,6 +5,7 @@ Cop/UserAdmin:
- 'app/controllers/sessions_controller.rb'
- 'app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb'
- 'app/graphql/resolvers/admin/analytics/usage_trends/measurements_resolver.rb'
- 'app/models/concerns/protected_ref_access.rb'
- 'app/models/concerns/spammable.rb'
- 'app/models/merge_requests_closing_issues.rb'
- 'app/models/protected_branch.rb'
@ -15,7 +16,6 @@ Cop/UserAdmin:
- 'app/services/projects/fork_service.rb'
- 'app/services/users/build_service.rb'
- 'ee/app/controllers/ee/projects_controller.rb'
- 'ee/app/models/concerns/ee/protected_ref_access.rb'
- 'ee/app/models/ee/user.rb'
- 'ee/app/policies/ee/group_policy.rb'
- 'ee/app/services/ee/groups/create_service.rb'

View File

@ -147,7 +147,7 @@ export default {
<gl-loading-icon
v-if="labelsFetchInProgress"
class="labels-fetch-loading gl-align-items-center gl-w-full gl-h-full gl-mb-3"
size="lg"
size="sm"
/>
<template v-else>
<gl-dropdown-item

View File

@ -53,7 +53,7 @@ export default {
</script>
<template>
<div>
<div class="gl-mt-3" data-testid="embedded-labels-list">
<gl-label
v-for="label in sortedSelectedLabels"
:key="label.id"

View File

@ -1,5 +1,5 @@
<script>
import { GlFormGroup, GlIcon } from '@gitlab/ui';
import { GlFormGroup } from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import LabelsSelect from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue';
import { __ } from '~/locale';
@ -7,7 +7,6 @@ import { __ } from '~/locale';
export default {
components: {
GlFormGroup,
GlIcon,
LabelsSelect,
},
inject: [
@ -50,10 +49,6 @@ export default {
<gl-form-group class="row" label-class="gl-display-none">
<label class="col-12 gl-display-flex gl-align-center gl-mb-1">
{{ $options.i18n.fieldLabel }}
<div class="gl-ml-3">
<gl-icon name="labels" />
<span class="gl-font-base gl-line-height-24">{{ selectedLabels.length }}</span>
</div>
</label>
<div class="col-12">
<div class="issuable-form-select-holder">

View File

@ -454,7 +454,7 @@ class Projects::IssuesController < Projects::ApplicationController
def require_incident_for_incident_routes
return unless params[:incident_tab].present?
return if issue.incident?
return if issue.work_item_type&.incident?
# Redirect instead of 404 to gracefully handle
# issue type changes

View File

@ -35,7 +35,7 @@ module Mutations
end
def authorize!(object)
raise_noteable_not_incident! if object && !object.try(:incident?)
raise_noteable_not_incident! if object && !object.try(:incident_type_issue?)
super
end

View File

@ -8,7 +8,7 @@ module Resolvers
type ::Types::Achievements::UserAchievementType.connection_type, null: true
def resolve_with_lookahead
user_achievements = object.user_achievements.not_revoked
user_achievements = object.user_achievements.not_revoked.order_by_id_asc
apply_lookahead(user_achievements)
end

View File

@ -45,7 +45,9 @@ module Types
Types::Achievements::UserAchievementType.connection_type,
null: true,
alpha: { milestone: '15.10' },
description: "Recipients for the achievement."
description: "Recipients for the achievement.",
extras: [:lookahead],
resolver: ::Resolvers::Achievements::UserAchievementsResolver
def avatar_url
object.avatar_url(only_path: false)

View File

@ -72,7 +72,8 @@ module FormHelper
multi_select: true,
'input-meta': 'name',
'always-show-selectbox': true,
current_user_info: UserSerializer.new.represent(current_user)
current_user_info: UserSerializer.new.represent(current_user),
testid: 'assignee-ids-dropdown-toggle'
}
}

View File

@ -274,7 +274,7 @@ module IssuablesHelper
end
def incident_only_initial_data(issue)
return {} unless issue.incident?
return {} unless issue.incident_type_issue?
{
hasLinkedAlerts: issue.alert_management_alerts.any?,
@ -396,6 +396,35 @@ module IssuablesHelper
}
end
def issuable_label_selector_data(project, issuable)
initial_labels = issuable.labels.map do |label|
{
__typename: "Label",
id: label.id,
title: label.title,
description: label.description,
color: label.color,
text_color: label.text_color
}
end
filter_base_path =
if issuable.issuable_type == "merge_request"
project_merge_requests_path(project)
else
project_issues_path(project)
end
{
field_name: "#{issuable.class.model_name.param_key}[label_ids][]",
full_path: project.full_path,
initial_labels: initial_labels.to_json,
issuable_type: issuable.issuable_type,
labels_filter_base_path: filter_base_path,
labels_manage_path: project_labels_path(project)
}
end
private
def sidebar_gutter_collapsed?

View File

@ -155,7 +155,7 @@ module IssuesHelper
def issue_header_actions_data(project, issuable, current_user, issuable_sidebar)
new_issuable_params = { issue: {}, add_related_issue: issuable.iid }
if issuable.incident?
if issuable.work_item_type&.incident?
new_issuable_params[:issuable_template] = 'incident'
new_issuable_params[:issue][:issue_type] = 'incident'
end

View File

@ -15,6 +15,7 @@ module Achievements
optional: true
scope :not_revoked, -> { where(revoked_by_user_id: nil) }
scope :order_by_id_asc, -> { order(id: :asc) }
def revoked?
revoked_by_user_id.present?

View File

@ -174,6 +174,10 @@ module Issuable
end
end
def issuable_type
self.class.name.underscore
end
# We want to use optimistic lock for cases when only title or description are involved
# http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
def locking_enabled?
@ -197,15 +201,15 @@ module Issuable
end
def supports_severity?
incident?
incident_type_issue?
end
def supports_escalation?
incident?
incident_type_issue?
end
def incident?
is_a?(Issue) && super
def incident_type_issue?
is_a?(Issue) && work_item_type&.incident?
end
def supports_issue_type?

View File

@ -6,18 +6,24 @@ module ProtectedRefAccess
class_methods do
def human_access_levels
{
Gitlab::Access::DEVELOPER => "Developers + Maintainers",
Gitlab::Access::MAINTAINER => "Maintainers",
Gitlab::Access::NO_ACCESS => "No one"
}
Gitlab::Access::DEVELOPER => 'Developers + Maintainers',
Gitlab::Access::MAINTAINER => 'Maintainers',
Gitlab::Access::ADMIN => 'Instance admins',
Gitlab::Access::NO_ACCESS => 'No one'
}.slice(*allowed_access_levels)
end
def allowed_access_levels
[
Gitlab::Access::MAINTAINER,
levels = [
Gitlab::Access::DEVELOPER,
Gitlab::Access::MAINTAINER,
Gitlab::Access::ADMIN,
Gitlab::Access::NO_ACCESS
]
return levels unless Gitlab.com?
levels.excluding(Gitlab::Access::ADMIN)
end
def humanize(access_level)
@ -47,6 +53,7 @@ module ProtectedRefAccess
def check_access(current_user)
return false if current_user.nil? || no_access?
return current_user.admin? if admin_access?
yield if block_given?
@ -55,6 +62,10 @@ module ProtectedRefAccess
private
def admin_access?
role? && access_level == ::Gitlab::Access::ADMIN
end
def no_access?
role? && access_level == Gitlab::Access::NO_ACCESS
end

View File

@ -40,6 +40,7 @@ class Issue < ApplicationRecord
DueNextMonthAndPreviousTwoWeeks = DueDateStruct.new('Due Next Month And Previous Two Weeks', 'next_month_and_previous_two_weeks').freeze
IssueTypeOutOfSyncError = Class.new(StandardError)
ForbiddenColumnUsed = Class.new(StandardError)
SORTING_PREFERENCE_FIELD = :issues_sort
MAX_BRANCH_TEMPLATE = 255
@ -139,6 +140,28 @@ class Issue < ApplicationRecord
enum issue_type: WorkItems::Type.base_types
# TODO: Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/402699
WorkItems::Type.base_types.each do |base_type, _value|
define_method "#{base_type}?".to_sym do
error_message = <<~ERROR
`#{base_type}?` uses the `issue_type` column underneath. As we want to remove the column,
its usage is forbidden. You should use the `work_item_types` table instead.
# Before
issue.requirement? => true
# After
issue.work_item_type.requirement? => true
More details in https://gitlab.com/groups/gitlab-org/-/epics/10529
ERROR
raise ForbiddenColumnUsed, error_message
end
end
alias_method :issuing_parent, :project
alias_attribute :issuing_parent_id, :project_id

View File

@ -15,6 +15,7 @@ class PersonalAccessToken < ApplicationRecord
# PATs are 20 characters + optional configurable settings prefix (0..20)
TOKEN_LENGTH_RANGE = (20..40).freeze
MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS = 365
serialize :scopes, Array # rubocop:disable Cop/ActiveRecordSerialize
@ -48,6 +49,7 @@ class PersonalAccessToken < ApplicationRecord
validates :scopes, presence: true
validate :validate_scopes
validate :expires_at_before_instance_max_expiry_date, on: :create
def revoke!
update!(revoked: true)
@ -57,6 +59,19 @@ class PersonalAccessToken < ApplicationRecord
!revoked? && !expired?
end
# fall back to default value until background migration has updated all
# existing PATs and we can add a validation
# https://gitlab.com/gitlab-org/gitlab/-/issues/369123
def expires_at=(value)
datetime = if Feature.enabled?(:default_pat_expiration)
value.presence || MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now
else
value
end
super(datetime)
end
override :simple_sorts
def self.simple_sorts
super.merge(
@ -108,6 +123,15 @@ class PersonalAccessToken < ApplicationRecord
def prefix_from_application_current_settings
self.class.token_prefix
end
def expires_at_before_instance_max_expiry_date
return unless Feature.enabled?(:default_pat_expiration)
return unless expires_at
if expires_at > MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now
errors.add(:expires_at, _('must expire in 365 days'))
end
end
end
PersonalAccessToken.prepend_mod_with('PersonalAccessToken')

View File

@ -14,7 +14,7 @@ class IssuablePolicy < BasePolicy
condition(:is_author) { @subject&.author == @user }
condition(:is_incident) { @subject.incident? }
condition(:is_incident) { @subject.incident_type_issue? }
desc "Issuable is hidden"
condition(:hidden, scope: :subject) { @subject.hidden? }

View File

@ -5,7 +5,7 @@ module IncidentManagement
include Gitlab::Utils::UsageData
def track_incident_action(current_user, target, action)
return unless target.incident?
return unless target.incident_type_issue?
event = "incident_management_#{action}"
track_usage_event(event, current_user.id)

View File

@ -110,7 +110,7 @@ module Issues
issue.namespace.execute_hooks(issue_data, hooks_scope)
issue.namespace.execute_integrations(issue_data, hooks_scope)
execute_incident_hooks(issue, issue_data) if issue.incident?
execute_incident_hooks(issue, issue_data) if issue.work_item_type&.incident?
end
# We can remove this code after proposal in

View File

@ -93,7 +93,7 @@ module Issues
end
def resolve_incident(issue)
return unless issue.incident?
return unless issue.work_item_type&.incident?
status = issue.incident_management_issuable_escalation_status || issue.build_incident_management_issuable_escalation_status

View File

@ -112,7 +112,7 @@ module Issues
attr_reader :spam_params, :extra_params
def create_timeline_event(issue)
return unless issue.incident?
return unless issue.work_item_type&.incident?
IncidentManagement::TimelineEvents::CreateService.create_incident(issue, current_user)
end

View File

@ -27,7 +27,7 @@ module Issues
end
def perform_incident_management_actions(issue)
return unless issue.incident?
return unless issue.work_item_type&.incident?
create_timeline_event(issue)
end

View File

@ -100,7 +100,15 @@ module ResourceAccessTokens
end
def create_membership(resource, user, access_level)
resource.add_member(user, access_level, expires_at: params[:expires_at])
resource.add_member(user, access_level, expires_at: default_pat_expiration)
end
def default_pat_expiration
if Feature.enabled?(:default_pat_expiration)
params[:expires_at].presence || PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now
else
params[:expires_at]
end
end
def log_event(token)

View File

@ -55,7 +55,7 @@ module ResourceEvents
end
def create_timeline_events_from(added_labels: [], removed_labels: [])
return unless resource.incident?
return unless resource.incident_type_issue?
IncidentManagement::TimelineEvents::CreateService.change_labels(
resource,

View File

@ -1,4 +1,4 @@
- return if @issue.incident?
- return if @issue.work_item_type&.incident?
- requirements_link_url = help_page_path('user/project/issues/design_management', anchor: 'requirements')
- requirements_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: requirements_link_url }

View File

@ -37,12 +37,15 @@
.issuable-form-select-holder
= render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]"
.form-group.row
= form.label :label_ids, _('Labels'), class: "col-12"
= form.hidden_field :label_ids, multiple: true, value: ''
.col-12
.issuable-form-select-holder
= render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false }, dropdown_title: "Select label"
- if Feature.enabled?(:visible_label_selection_on_metadata, project)
.js-issuable-form-label-selector{ data: issuable_label_selector_data(project, issuable) }
- else
.form-group.row
= form.label :label_ids, _('Labels'), class: "col-12"
= form.hidden_field :label_ids, multiple: true, value: ''
.col-12
.issuable-form-select-holder
= render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false }, dropdown_title: "Select label"
= render_if_exists "shared/issuable/form/merge_request_blocks", issuable: issuable, form: form

View File

@ -8,7 +8,7 @@
.issuable-form-select-holder.form-group.gl-mb-0.gl-display-block
#js-type-select{ data: issuable_type_selector_data(issuable) }
- if issuable.incident?
- if issuable.incident_type_issue?
%p.form-text.text-muted
- incident_docs_url = help_page_path('operations/incident_management/incidents.md')
- incident_docs_start = format('<a href="%{url}" target="_blank" rel="noopener noreferrer">', url: incident_docs_url)

View File

@ -0,0 +1,7 @@
name: default_pat_expiration
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120213
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/410440
milestone: '16.0'
type: development
group: group::authentication and authorization
default_enabled: true

View File

@ -0,0 +1,8 @@
---
name: visible_label_selection_on_metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88908
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364534
milestone: '16.0'
type: development
group: "group::ux paper cuts"
default_enabled: false

View File

@ -0,0 +1,11 @@
- title: "Security report schemas version 14.x.x"
announcement_milestone: "15.3"
removal_milestone: "16.0"
breaking_change: true
reporter: abellucci
stage: Govern
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366477
body: |
Version 14.x.x [security report schemas](https://gitlab.com/gitlab-org/security-products/security-report-schemas) have been removed.
Security reports that use schema version 14.x.x will cause an error in the pipeline's **Security** tab. For more information, refer to [security report validation](https://docs.gitlab.com/ee/user/application_security/#security-report-validation).
tiers: [Ultimate]

View File

@ -4,7 +4,7 @@
breaking_change: true # (required) Change to false if this is not a breaking change.
reporter: derekferguson # (required) GitLab username of the person reporting the removal
stage: Secure # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383467 # (required) Link to the deprecation issue in GitLab
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383467 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The variables `DAST_API_HOST_OVERRIDE` and `DAST_API_SPECIFICATION` have been removed from use for DAST API scans.

View File

@ -15,5 +15,5 @@
In your new Grafana instance, you can [configure the GitLab provided Prometheus as a data source](https://docs.gitlab.com/ee/administration/monitoring/performance/grafana_configuration.html#integration-with-gitlab-ui)
and [connect Grafana to the GitLab UI](https://docs.gitlab.com/ee/administration/monitoring/performance/grafana_configuration.html#integration-with-gitlab-ui).
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
documentation_url: https://docs.gitlab.com/ee/administration/monitoring/performance/grafana_configuration.html

View File

@ -2,7 +2,7 @@
announcement_milestone: "15.9" # (required) The milestone when this feature was first announced as deprecated.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: jreporter # (required) GitLab username of the person reporting the deprecation
reporter: jreporter # (required) GitLab username of the person reporting the deprecation
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/395708 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.

View File

@ -0,0 +1,19 @@
- title: "Non-expiring access tokens no longer supported"
announcement_milestone: "15.4" # (required) The milestone when this feature was deprecated.
removal_milestone: "16.0" # (required) The milestone when this feature is being removed.
breaking_change: true # (required) Change to false if this is not a breaking change.
reporter: jessieay # (required) GitLab username of the person reporting the removal
stage: Manage # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369123
body: | # (required) Do not modify this line, instead modify the lines below.
Currently, you can create access tokens that have no expiration date. These access tokens are valid indefinitely, which presents a security risk if the access token is
divulged. Because expiring access tokens are better, from GitLab 15.4 we [populate a default expiration date](https://gitlab.com/gitlab-org/gitlab/-/issues/348660).
In GitLab 16.0, any personal, project, or group access token that does not have an expiration date will automatically have an expiration date set at 365 days later than the current date.
#
# OPTIONAL FIELDS
#
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
documentation_url: # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg

View File

@ -13,5 +13,5 @@
to PostgreSQL 13.
- Using an externally-provided PostgreSQL 12, you must upgrade to PostgreSQL 13 or later to meet the
[minimum version requirements](https://docs.gitlab.com/ee/install/requirements.html#postgresql-requirements).
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
documentation_url: https://docs.gitlab.com/ee/administration/package_information/postgresql_versions.html

View File

@ -4,7 +4,7 @@
breaking_change: false # (required) Change to false if this is not a breaking change.
reporter: DarrenEastman # (required) GitLab username of the person reporting the removal
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379064 # (required) Link to the deprecation issue in GitLab
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379064 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
In GitLab 13.6 and later, users can [specify any runner configuration in the GitLab Runner Helm chart](https://docs.gitlab.com/runner/install/kubernetes.html). When this features was released, we deprecated the fields in the GitLab Helm Chart configuration specific to the runner. As of v1.0 of the GitLab Runner Helm chart (GitLab 16.0), the following fields have been removed and are no longer supported:

View File

@ -4,6 +4,6 @@
breaking_change: false # (required) Change to false if this is not a breaking change.
reporter: DarrenEastman # (required) GitLab username of the person reporting the removal
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab-runner/-/issues/31001 # (required) Link to the deprecation issue in GitLab
issue_url: https://gitlab.com/gitlab-org/gitlab-runner/-/issues/31001 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
As of GitLab 16.0, GitLab Runner images based on Windows Server 2004 and 20H2 will not be provided as these operating systems are end-of-life.

View File

@ -4,7 +4,7 @@
breaking_change: true # (required) Change to false if this is not a breaking change.
reporter: DarrenEastman # (required) GitLab username of the person reporting the removal
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344648 # (required) Link to the deprecation issue in GitLab
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344648 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
In GitLab 16.0 and later, the GraphQL query for runners will no longer return the statuses `PAUSED` and `ACTIVE`.

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class RemoveShimoZentaoIntegrationRecords < Gitlab::Database::Migration[2.1]
TYPES = %w[Integrations::Shimo Integrations::Zentao]
BATCH_SIZE = 100
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
def up
return if Gitlab.jh?
define_batchable_model(:integrations)
.where(type_new: TYPES)
.each_batch(of: BATCH_SIZE) { |relation, _index| relation.delete_all }
end
def down
# no-op
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
class BackfillCorrectedSecureFilesExpirations < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_ci
BATCH_SIZE = 1000
def up
each_batch_range('ci_secure_files', of: BATCH_SIZE) do |min, max|
sql = <<-SQL
SELECT id
FROM ci_secure_files
WHERE name ILIKE any (array['%.cer', '%.p12'])
AND ci_secure_files.id BETWEEN #{min} AND #{max}
SQL
rows = execute(sql)
rows.each do |row|
::Ci::ParseSecureFileMetadataWorker.perform_async(row["id"])
end
end
end
def down; end
end

View File

@ -0,0 +1 @@
9e822fbc2c7ce8044d0b38c5f1a9056431792e83fc9ed83056444c094e16c484

View File

@ -0,0 +1 @@
eaec908173fb60b88867e14c73c6ba7d6079742bae7ead59fa021d6d57e622da

View File

@ -855,7 +855,7 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ----------- |
| `discussion_id` | integer | yes | The ID of a discussion item. |
| `discussion_id` | string | yes | The ID of a discussion item. |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `merge_request_iid` | integer | yes | The IID of a merge request. |
@ -1023,7 +1023,7 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `discussion_id` | integer | yes | The ID of a thread. |
| `discussion_id` | string | yes | The ID of a thread. |
| `merge_request_iid` | integer | yes | The IID of a merge request. |
| `resolved` | boolean | yes | Resolve or unresolve the discussion. |
@ -1047,7 +1047,7 @@ Parameters:
| ------------------- | -------------- | -------- | ----------- |
| `body` | string | yes | The content of the note or reply. |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `discussion_id` | integer | yes | The ID of a thread. |
| `discussion_id` | string | yes | The ID of a thread. |
| `merge_request_iid` | integer | yes | The IID of a merge request. |
| `note_id` | integer | yes | The ID of a thread note. |
| `created_at` | string | no | Date time string, ISO 8601 formatted, such as `2016-03-11T03:45:40Z`. Requires administrator or project/group owner rights. |
@ -1069,7 +1069,7 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ----------- |
| `discussion_id` | integer | yes | The ID of a thread. |
| `discussion_id` | string | yes | The ID of a thread. |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `merge_request_iid` | integer | yes | The IID of a merge request. |
| `note_id` | integer | yes | The ID of a thread note. |
@ -1100,7 +1100,7 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ----------- |
| `discussion_id` | integer | yes | The ID of a thread. |
| `discussion_id` | string | yes | The ID of a thread. |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `merge_request_iid` | integer | yes | The IID of a merge request. |
| `note_id` | integer | yes | The ID of a thread note. |

View File

@ -1463,6 +1463,7 @@ Input type: `CiAiGenerateConfigInput`
| ---- | ---- | ----------- |
| <a id="mutationciaigenerateconfigclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationciaigenerateconfigerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationciaigenerateconfigusermessage"></a>`userMessage` | [`AiMessageType`](#aimessagetype) | User chat message. |
### `Mutation.ciCdSettingsUpdate`

View File

@ -73,3 +73,41 @@ Some additional information is included at the bottom of the comment:
migration (ending in `.log`) are available there, and only accessible by
database maintainers or with an access request. Details of the specific
batched background migration batches sampled are also available.
## Test changes to the database testing pipeline
To test a change to the database testing pipeline itself, you need:
1. A merge request against GitLab Org.
1. The change to be tested must be present on a branch on GitLab Ops.
Use this self-documented script to test a merge request on GitLab Org against an arbitrary branch on GitLab Ops:
```shell
#! /usr/bin/env bash
# The following must be set on a per-invocation basis:
TESTING_TRIGGER_TOKEN='[REDACTED]' # Testing trigger token created in the CI section of the project
CI_COMMIT_REF_NAME='55-post-notice-on-failure' # The branch on ops that you want to run against
CI_MERGE_REQUEST_IID='117901' # Merge request ID of the MR on gitlab.com that you want to test
SHA="fed6dd8a58d75a0e053a4972765b4fc08c5814a3" # The commit SHA of the HEAD of the branch you want to test on gitlab-org/gitlab
# The following should not be changed between invocations:
CI_JOB_URL='https://gitlab.com/gitlab-org/database-team/gitlab-com-database-testing/-/jobs/1590162939'
# It doesn't appear that CI_JOB_URL has to be set to anything in particular for the pipeline to run
# successfully, but this would normally be the URL to the upstream job that invokes the DB testing pipeline.
CI_MERGE_REQUEST_PROJECT_ID='278964' # gitlab-org/gitlab numeric ID. Shouldn't change.
CI_PROJECT_ID="gitlab-org/gitlab" # The slug identifying gitlab-org/gitlab.
curl --verbose --request POST \
--form "token=$TESTING_TRIGGER_TOKEN" \
--form "ref=$CI_COMMIT_REF_NAME" \
--form "variables[TOP_UPSTREAM_MERGE_REQUEST_IID]=$CI_MERGE_REQUEST_IID" \
--form "variables[TOP_UPSTREAM_MERGE_REQUEST_PROJECT_ID]=$CI_MERGE_REQUEST_PROJECT_ID" \
--form "variables[TOP_UPSTREAM_SOURCE_JOB]=$CI_JOB_URL" \
--form "variables[TOP_UPSTREAM_SOURCE_PROJECT]=$CI_PROJECT_ID" \
--form "variables[VALIDATION_PIPELINE]=true" \
--form "variables[GITLAB_COMMIT_SHA]=$SHA" \
--form "variables[TRIGGER_SOURCE]=$CI_JOB_URL" \
"https://ops.gitlab.net/api/v4/projects/429/trigger/pipeline"
```

View File

@ -278,9 +278,9 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
### 15.11.0
- Upgrades to GitLab 15.11 directly from GitLab versions 15.5.0 and earlier on self-managed installs will fail due to a missing migration until the fix for [issue 408304](https://gitlab.com/gitlab-org/gitlab/-/issues/408304) is released in an upcoming patch release. Affected users wanting to upgrade to 15.11.x can either:
- Upgrades to GitLab 15.11 directly from GitLab versions 15.5.0 and earlier on self-managed installs will fail due to a missing migration until the fix for [issue 408304](https://gitlab.com/gitlab-org/gitlab/-/issues/408304) is released in version 15.11.3. Affected users wanting to upgrade to 15.11 can either:
- Perform an intermediate upgrade to any version between 15.5 and 15.10 before upgrading to 15.11, or
- Target the forthcoming patch release.
- Target version 15.11.3 or later.
### 15.10.5

View File

@ -190,6 +190,17 @@ The [**Maximum number of active pipelines per project** limit](https://docs.gitl
- [**Pipelines rate limits**](https://docs.gitlab.com/ee/user/admin_area/settings/rate_limit_on_pipelines_creation.html).
- [**Total number of jobs in currently active pipelines**](https://docs.gitlab.com/ee/user/admin_area/settings/continuous_integration.html#set-cicd-limits).
### Non-expiring access tokens no longer supported
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
Currently, you can create access tokens that have no expiration date. These access tokens are valid indefinitely, which presents a security risk if the access token is
divulged. Because expiring access tokens are better, from GitLab 15.4 we [populate a default expiration date](https://gitlab.com/gitlab-org/gitlab/-/issues/348660).
In GitLab 16.0, any personal, project, or group access token that does not have an expiration date will automatically have an expiration date set at 365 days later than the current date.
### Non-standard default Redis ports are no longer supported
WARNING:
@ -323,6 +334,15 @@ Review the details carefully before upgrading.
From GitLab 15.9, all Release links are external. The `external` field in the Releases and Release link APIs was deprecated in 15.9, and removed in GitLab 16.0.
### Security report schemas version 14.x.x
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
Version 14.x.x [security report schemas](https://gitlab.com/gitlab-org/security-products/security-report-schemas) have been removed.
Security reports that use schema version 14.x.x will cause an error in the pipeline's **Security** tab. For more information, refer to [security report validation](https://docs.gitlab.com/ee/user/application_security/#security-report-validation).
### Stop publishing GitLab Runner images based on Windows Server 2004 and 20H2
As of GitLab 16.0, GitLab Runner images based on Windows Server 2004 and 20H2 will not be provided as these operating systems are end-of-life.

View File

@ -28,11 +28,7 @@ associated with a group rather than a project or user.
In self-managed instances, group access tokens are subject to the same [maximum lifetime limits](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) as personal access tokens if the limit is set.
WARNING:
The ability to create group access tokens without expiry was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369122) in GitLab 15.4 and is planned for removal in GitLab
16.0. When this ability is removed, existing group access tokens without an expiry are planned to have an expiry added.
The automatic adding of an expiry occurs on GitLab.com during the 16.0 milestone. The automatic adding of an expiry
occurs on self-managed instances when they are upgraded to GitLab 16.0. This change is a breaking change.
The ability to create group access tokens without an expiry date was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369122) in GitLab 15.4 and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/392855) in GitLab 16.0. In GitLab 16.0 and later, existing group access tokens without an expiry date are automatically given an expiry date 365 days later than the current date. The automatic adding of an expiry date occurs on GitLab.com during the 16.0 milestone. The automatic adding of an expiry date occurs on self-managed instances when they are upgraded to GitLab 16.0. This change is a breaking change.
You can use group access tokens:
@ -52,13 +48,18 @@ configured for personal access tokens.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214045) in GitLab 14.7.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days and default role of Guest is populated in the UI.
> - Ability to create non-expiring group access tokens [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/392855) in GitLab 16.0.
To create a group access token:
1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Access Tokens**.
1. Enter a name. The token name is visible to any user with permissions to view the group.
1. Optional. Enter an expiry date for the token. The token will expire on that date at midnight UTC. An instance-wide [maximum lifetime](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) setting can limit the maximum allowable lifetime in self-managed instances.
1. Enter an expiry date for the token:
- The token expires on that date at midnight UTC.
- If you do not enter an expiry date, the expiry date is automatically set to 365 days later than the current date.
- By default, this date can be a maximum of 365 days later than the current date.
- An instance-wide [maximum lifetime](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) setting can limit the maximum allowable lifetime in self-managed instances.
1. Select a role for the token.
1. Select the [desired scopes](#scopes-for-a-group-access-token).
1. Select **Create group access token**.

View File

@ -20,11 +20,7 @@ Personal access tokens can be an alternative to [OAuth2](../../api/oauth2.md) an
In both cases, you authenticate with a personal access token in place of your password.
WARNING:
The ability to create personal access tokens without expiry was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369122) in GitLab 15.4 and is planned for removal in GitLab
16.0. When this ability is removed, existing personal access tokens without an expiry are planned to have an expiry added.
The automatic adding of an expiry occurs on GitLab.com during the 16.0 milestone. The automatic adding of an expiry
occurs on self-managed instances when they are upgraded to GitLab 16.0. This change is a breaking change.
The ability to create personal access tokens without expiry was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369122) in GitLab 15.4 and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/392855) in GitLab 16.0. In GitLab 16.0 and later, existing personal access tokens without an expiry date are automatically given an expiry date of 365 days later than the current date. The automatic adding of an expiry date occurs on GitLab.com during the 16.0 milestone. The automatic adding of an expiry date occurs on self-managed instances when they are upgraded to GitLab 16.0. This change is a breaking change.
Personal access tokens are:
@ -47,14 +43,18 @@ Use impersonation tokens to automate authentication as a specific user.
## Create a personal access token
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days is populated in the UI.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days is populated in the UI.
> - Ability to create non-expiring personal access tokens [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/392855) in GitLab 16.0.
You can create as many personal access tokens as you like.
1. In the upper-right corner, select your avatar.
1. Select **Edit profile**.
1. On the left sidebar, select **Access Tokens**.
1. Enter a name and optional expiry date for the token.
1. Enter a name and expiry date for the token.
- The token expires on that date at midnight UTC.
- If you do not enter an expiry date, the expiry date is automatically set to 365 days later than the current date.
- By default, this date can be a maximum of 365 days later than the current date.
1. Select the [desired scopes](#personal-access-token-scopes).
1. Select **Create personal access token**.

View File

@ -28,11 +28,7 @@ and [personal access tokens](../../profile/personal_access_tokens.md).
In self-managed instances, project access tokens are subject to the same [maximum lifetime limits](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) as personal access tokens if the limit is set.
WARNING:
The ability to create project access tokens without expiry was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369122) in GitLab 15.4 and is planned for removal in GitLab
16.0. When this ability is removed, existing project access tokens without an expiry are planned to have an expiry added.
The automatic adding of an expiry occurs on GitLab.com during the 16.0 milestone. The automatic adding of an expiry
occurs on self-managed instances when they are upgraded to GitLab 16.0. This change is a breaking change.
The ability to create project access tokens without expiry was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369122) in GitLab 15.4 and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/392855) in GitLab 16.0. In GitLab 16.0 and later, existing project access tokens without an expiry date are automatically given an expiry date of 365 days later than the current date. The automatic adding of an expiry date occurs on GitLab.com during the 16.0 milestone. The automatic adding of an expiry date occurs on self-managed instances when they are upgraded to GitLab 16.0. This change is a breaking change.
You can use project access tokens:
@ -52,14 +48,18 @@ configured for personal access tokens.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89114) in GitLab 15.1, Owners can select Owner role for project access tokens.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days and default role of Guest is populated in the UI.
> - Ability to create non-expiring project access tokens [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/392855) in GitLab 16.0.
To create a project access token:
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Access Tokens**.
1. Enter a name. The token name is visible to any user with permissions to view the project.
1. Optional. Enter an expiry date for the token. The token expires on that date at midnight UTC. An instance-wide [maximum lifetime](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) setting can limit the maximum allowable lifetime in self-managed instances.
1. Enter an expiry date for the token.
- The token expires on that date at midnight UTC.
- If you do not enter an expiry date, the expiry date is automatically set to 365 days later than the current date.
- By default, this date can be a maximum of 365 days later than the current date.
- An instance-wide [maximum lifetime](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) setting can limit the maximum allowable lifetime in self-managed instances.
1. Select a role for the token.
1. Select the [desired scopes](#scopes-for-a-project-access-token).
1. Select **Create project access token**.

View File

@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Workspaces (Beta) **(PREMIUM)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10122) in GitLab 16.0 [with a flag](../../administration/feature_flags.md) named `remote_development_feature_flag`. Disabled by default.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10122) in GitLab 16.0 [with a flag](../../administration/feature_flags.md) named `remote_development_feature_flag`. Enabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `remote_development_feature_flag`. On GitLab.com, this feature is not available. The feature is not ready for production use.

View File

@ -16,6 +16,7 @@ module Gitlab
DEVELOPER = 30
MAINTAINER = 40
OWNER = 50
ADMIN = 60
# Branch protection settings
PROTECTION_NONE = 0

View File

@ -150,7 +150,7 @@ module Gitlab
if data['gitlab_schema'].nil?
raise(
UnknownSchemaError,
"#{file_path} must specify a valid gitlab_schema for #{key_name}." \
"#{file_path} must specify a valid gitlab_schema for #{key_name}. " \
"See https://docs.gitlab.com/ee/development/database/database_dictionary.html"
)
end

View File

@ -255,7 +255,7 @@ module Gitlab
execution_message { _('Issue has been promoted to incident') }
types Issue
condition do
!quick_action_target.incident? &&
!quick_action_target.work_item_type&.incident? &&
current_user.can?(:"set_#{quick_action_target.issue_type}_metadata", quick_action_target)
end
command :promote_to_incident do
@ -298,7 +298,7 @@ module Gitlab
params '<timeline comment> | <date(YYYY-MM-DD)> <time(HH:MM)>'
types Issue
condition do
quick_action_target.incident? &&
quick_action_target.work_item_type&.incident? &&
current_user.can?(:admin_incident_management_timeline_event, quick_action_target)
end
parse_params do |event_params|

View File

@ -502,6 +502,7 @@ namespace :gitlab do
milestone = version.release.segments.first(2).join('.')
classes = {}
ignored_tables = %w[p_ci_builds]
Gitlab::Database.database_base_models.each do |_, model_class|
tables = model_class.connection.tables
@ -524,6 +525,7 @@ namespace :gitlab do
sources.each do |source_name|
next if source_name.start_with?('_test_') # Ignore test tables
next if ignored_tables.include?(source_name)
database = model_class.connection_db_config.name
file = dictionary_file_path(source_name, views, database)

View File

@ -50979,7 +50979,7 @@ msgstr ""
msgid "Workspaces"
msgstr ""
msgid "Workspaces|A workspace is a virtual sandbox environment for your code in GitLab. You can create a workspace on its own or as part of a project."
msgid "Workspaces|A workspace is a virtual sandbox environment for your code in GitLab. You can create a workspace for a public project."
msgstr ""
msgid "Workspaces|Cancel"
@ -53917,6 +53917,9 @@ msgstr ""
msgid "must contain only a discord user ID."
msgstr ""
msgid "must expire in 365 days"
msgstr ""
msgid "must have a repository"
msgstr ""

View File

@ -282,7 +282,7 @@
"webpack-dev-server": "4.15.0",
"xhr-mock": "^2.5.1",
"yarn-check-webpack-plugin": "^1.2.0",
"yarn-deduplicate": "^6.0.0"
"yarn-deduplicate": "^6.0.2"
},
"blockedDependencies": {
"bootstrap-vue": "https://docs.gitlab.com/ee/development/fe_guide/dependencies.html#bootstrapvue"

View File

@ -18,6 +18,7 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
let_it_be(:confidential_issue) { create(:issue, project: project, assignees: [user], milestone: milestone, confidential: true) }
let(:current_user) { user }
let(:visible_label_selection_on_metadata) { false }
before_all do
project.add_maintainer(user)
@ -27,6 +28,7 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
before do
stub_licensed_features(multiple_issue_assignees: false, issue_weights: false)
stub_feature_flags(visible_label_selection_on_metadata: visible_label_selection_on_metadata)
sign_in(current_user)
end
@ -114,74 +116,232 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
end
end
it 'allows user to create new issue' do
fill_in 'issue_title', with: 'title'
fill_in 'issue_description', with: 'title'
context 'with the visible_label_selection_on_metadata feature flag enabled' do
let(:visible_label_selection_on_metadata) { true }
expect(find('a', text: 'Assign to me')).to be_visible
click_button 'Unassigned'
it 'allows user to create new issue' do
fill_in 'issue_title', with: 'title'
fill_in 'issue_description', with: 'title'
wait_for_requests
expect(find('a', text: 'Assign to me')).to be_visible
click_button 'Unassigned'
page.within '.dropdown-menu-user' do
click_link user2.name
end
expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s)
page.within '.js-assignee-search' do
expect(page).to have_content user2.name
end
expect(find('a', text: 'Assign to me')).to be_visible
wait_for_requests
click_link 'Assign to me'
assignee_ids = page.all('input[name="issue[assignee_ids][]"]', visible: false)
page.within '.dropdown-menu-user' do
click_link user2.name
end
expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s)
page.within '.js-assignee-search' do
expect(page).to have_content user2.name
end
expect(find('a', text: 'Assign to me')).to be_visible
expect(assignee_ids[0].value).to match(user.id.to_s)
click_link 'Assign to me'
assignee_ids = page.all('input[name="issue[assignee_ids][]"]', visible: false)
page.within '.js-assignee-search' do
expect(page).to have_content user.name
end
expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible
expect(assignee_ids[0].value).to match(user.id.to_s)
click_button 'Select milestone'
click_button milestone.title
expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
expect(page).to have_button milestone.title
page.within '.js-assignee-search' do
expect(page).to have_content user.name
end
expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible
click_button 'Labels'
page.within '.dropdown-menu-labels' do
click_link label.title
click_link label2.title
end
click_button 'Select milestone'
click_button milestone.title
expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
expect(page).to have_button milestone.title
find('.js-issuable-form-dropdown.js-label-select').click
page.within '.js-label-select' do
expect(page).to have_content label.title
end
expect(page.all('input[name="issue[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
expect(page.all('input[name="issue[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
click_button 'Create issue'
page.within '.issuable-sidebar' do
page.within '.assignee' do
expect(page).to have_content "Assignee"
click_button _('Select label')
wait_for_all_requests
page.within '[data-testid="sidebar-labels"]' do
click_button label.title
click_button label2.title
click_button _('Close')
wait_for_requests
page.within('[data-testid="embedded-labels-list"]') do
expect(page).to have_content(label.title)
expect(page).to have_content(label2.title)
end
end
page.within '.milestone' do
expect(page).to have_content milestone.title
click_button 'Create issue'
page.within '.issuable-sidebar' do
page.within '.assignee' do
expect(page).to have_content "Assignee"
end
page.within '.milestone' do
expect(page).to have_content milestone.title
end
page.within '.labels' do
expect(page).to have_content label.title
expect(page).to have_content label2.title
end
end
page.within '.labels' do
page.within '.breadcrumbs' do
issue = Issue.find_by(title: 'title')
expect(page).to have_text("Issues #{issue.to_reference}")
end
end
it 'correctly updates the dropdown toggle when removing a label' do
click_button _('Select label')
wait_for_all_requests
page.within '[data-testid="sidebar-labels"]' do
click_button label.title
click_button _('Close')
wait_for_requests
page.within('[data-testid="embedded-labels-list"]') do
expect(page).to have_content(label.title)
end
expect(page.find('.gl-dropdown-button-text')).to have_content(label.title)
end
click_button label.title, class: 'gl-dropdown-toggle'
wait_for_all_requests
page.within '[data-testid="sidebar-labels"]' do
click_button label.title, class: 'dropdown-item'
click_button _('Close')
wait_for_requests
expect(page).not_to have_selector('[data-testid="embedded-labels-list"]')
expect(page.find('.gl-dropdown-button-text')).to have_content(_('Select label'))
end
end
it 'clears label search input field when a label is selected', :js do
click_button _('Select label')
wait_for_all_requests
page.within '[data-testid="sidebar-labels"]' do
search_field = find('input[type="search"]')
search_field.native.send_keys(label.title)
expect(page).to have_css('.gl-search-box-by-type-clear')
click_button label.title, class: 'dropdown-item'
expect(page).not_to have_css('.gl-search-box-by-type-clear')
expect(search_field.value).to eq ''
end
end
end
context 'with the visible_label_selection_on_metadata feature flag disabled' do
let(:visible_label_selection_on_metadata) { false }
it 'allows user to create new issue' do
fill_in 'issue_title', with: 'title'
fill_in 'issue_description', with: 'title'
expect(find('a', text: 'Assign to me')).to be_visible
click_button 'Unassigned'
wait_for_requests
page.within '.dropdown-menu-user' do
click_link user2.name
end
expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s)
page.within '.js-assignee-search' do
expect(page).to have_content user2.name
end
expect(find('a', text: 'Assign to me')).to be_visible
click_link 'Assign to me'
assignee_ids = page.all('input[name="issue[assignee_ids][]"]', visible: false)
expect(assignee_ids[0].value).to match(user.id.to_s)
page.within '.js-assignee-search' do
expect(page).to have_content user.name
end
expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible
click_button 'Select milestone'
click_button milestone.title
expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
expect(page).to have_button milestone.title
click_button 'Labels'
page.within '.dropdown-menu-labels' do
click_link label.title
click_link label2.title
end
find('.js-issuable-form-dropdown.js-label-select').click
page.within '.js-label-select' do
expect(page).to have_content label.title
expect(page).to have_content label2.title
end
expect(page.all('input[name="issue[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
expect(page.all('input[name="issue[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
click_button 'Create issue'
page.within '.issuable-sidebar' do
page.within '.assignee' do
expect(page).to have_content "Assignee"
end
page.within '.milestone' do
expect(page).to have_content milestone.title
end
page.within '.labels' do
expect(page).to have_content label.title
expect(page).to have_content label2.title
end
end
page.within '.breadcrumbs' do
issue = Issue.find_by(title: 'title')
expect(page).to have_text("Issues #{issue.to_reference}")
end
end
page.within '.breadcrumbs' do
issue = Issue.find_by(title: 'title')
it 'correctly updates the dropdown toggle when removing a label' do
click_button 'Labels'
expect(page).to have_text("Issues #{issue.to_reference}")
page.within '.dropdown-menu-labels' do
click_link label.title
end
expect(find('.js-label-select')).to have_content(label.title)
page.within '.dropdown-menu-labels' do
click_link label.title
end
expect(find('.js-label-select')).to have_content('Labels')
end
it 'clears label search input field when a label is selected' do
click_button 'Labels'
page.within '.dropdown-menu-labels' do
search_field = find('input[type="search"]')
search_field.set(label2.title)
click_link label2.title
expect(search_field.value).to eq ''
end
end
end
@ -193,34 +353,6 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
end
end
it 'correctly updates the dropdown toggle when removing a label' do
click_button 'Labels'
page.within '.dropdown-menu-labels' do
click_link label.title
end
expect(find('.js-label-select')).to have_content(label.title)
page.within '.dropdown-menu-labels' do
click_link label.title
end
expect(find('.js-label-select')).to have_content('Labels')
end
it 'clears label search input field when a label is selected' do
click_button 'Labels'
page.within '.dropdown-menu-labels' do
search_field = find('input[type="search"]')
search_field.set(label2.title)
click_link label2.title
expect(search_field.value).to eq ''
end
end
it 'correctly updates the selected user when changing assignee' do
click_button 'Unassigned'
@ -426,42 +558,100 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
visit edit_project_issue_path(project, issue)
end
it 'allows user to update issue' do
expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user.id.to_s)
expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible
context 'with the visible_label_selection_on_metadata feature flag enabled' do
let(:visible_label_selection_on_metadata) { true }
page.within '.js-user-search' do
expect(page).to have_content user.name
end
it 'allows user to update issue' do
expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user.id.to_s)
expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible
expect(page).to have_button milestone.title
click_button 'Labels'
page.within '.dropdown-menu-labels' do
click_link label.title
click_link label2.title
end
page.within '.js-label-select' do
expect(page).to have_content label.title
end
expect(page.all('input[name="issue[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
expect(page.all('input[name="issue[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
click_button 'Save changes'
page.within '.issuable-sidebar' do
page.within '.assignee' do
page.within '.js-user-search' do
expect(page).to have_content user.name
end
page.within '.milestone' do
expect(page).to have_content milestone.title
expect(page).to have_button milestone.title
click_button _('Select label')
wait_for_all_requests
page.within '[data-testid="sidebar-labels"]' do
click_button label.title
click_button label2.title
click_button _('Close')
wait_for_requests
page.within('[data-testid="embedded-labels-list"]') do
expect(page).to have_content(label.title)
expect(page).to have_content(label2.title)
end
end
page.within '.labels' do
expect(page.all('input[name="issue[label_ids][]"]', visible: false)
.map(&:value))
.to contain_exactly(label.id.to_s, label2.id.to_s)
click_button 'Save changes'
page.within '.issuable-sidebar' do
page.within '.assignee' do
expect(page).to have_content user.name
end
page.within '.milestone' do
expect(page).to have_content milestone.title
end
page.within '.labels' do
expect(page).to have_content label.title
expect(page).to have_content label2.title
end
end
end
end
context 'with the visible_label_selection_on_metadata feature flag disabled' do
let(:visible_label_selection_on_metadata) { false }
it 'allows user to update issue' do
expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user.id.to_s)
expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible
page.within '.js-user-search' do
expect(page).to have_content user.name
end
expect(page).to have_button milestone.title
click_button 'Labels'
page.within '.dropdown-menu-labels' do
click_link label.title
click_link label2.title
end
page.within '.js-label-select' do
expect(page).to have_content label.title
expect(page).to have_content label2.title
end
expect(page.all('input[name="issue[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
expect(page.all('input[name="issue[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
click_button 'Save changes'
page.within '.issuable-sidebar' do
page.within '.assignee' do
expect(page).to have_content user.name
end
page.within '.milestone' do
expect(page).to have_content milestone.title
end
page.within '.labels' do
expect(page).to have_content label.title
expect(page).to have_content label2.title
end
end
end
end
@ -552,22 +742,55 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
visit new_project_issue_path(sub_group_project)
end
it 'creates project label from dropdown' do
click_button 'Labels'
context 'with the visible_label_selection_on_metadata feature flag enabled', :js do
let(:visible_label_selection_on_metadata) { true }
click_link 'Create project label'
it 'creates project label from dropdown' do
find('[data-testid="labels-select-dropdown-contents"] button').click
page.within '.dropdown-new-label' do
fill_in 'new_label_name', with: 'test label'
first('.suggest-colors-dropdown a').click
wait_for_all_requests
click_button 'Create'
page.within '[data-testid="sidebar-labels"]' do
click_button _('Create project label')
wait_for_requests
wait_for_requests
end
page.within '.js-labels-create' do
find('[data-testid="label-title-input"]').fill_in with: 'test label'
first('.suggest-colors-dropdown a').click
click_button 'Create'
wait_for_all_requests
end
page.within '.js-labels-list' do
expect(page).to have_button 'test label'
end
end
end
page.within '.dropdown-menu-labels' do
expect(page).to have_link 'test label'
context 'with the visible_label_selection_on_metadata feature flag disabled' do
let(:visible_label_selection_on_metadata) { false }
it 'creates project label from dropdown' do
click_button 'Labels'
click_link 'Create project label'
page.within '.dropdown-new-label' do
fill_in 'new_label_name', with: 'test label'
first('.suggest-colors-dropdown a').click
click_button 'Create'
wait_for_requests
end
page.within '.dropdown-menu-labels' do
expect(page).to have_link 'test label'
end
end
end
end

View File

@ -8,6 +8,8 @@ RSpec.describe "User creates issue", feature_category: :team_planning do
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) }
let(:visible_label_selection_on_metadata) { false }
context "when unauthenticated" do
before do
sign_out(:user)
@ -34,6 +36,7 @@ RSpec.describe "User creates issue", feature_category: :team_planning do
context "when signed in as guest", :js do
before do
stub_feature_flags(visible_label_selection_on_metadata: visible_label_selection_on_metadata)
project.add_guest(user)
sign_in(user)
@ -92,18 +95,50 @@ RSpec.describe "User creates issue", feature_category: :team_planning do
end
end
it "creates issue" do
issue_title = "500 error on profile"
context 'with the visible_label_selection_on_metadata feature flag enabled' do
let(:visible_label_selection_on_metadata) { true }
fill_in("Title", with: issue_title)
click_button("Label")
click_link(label_titles.first)
click_button("Create issue")
it "creates issue" do
issue_title = "500 error on profile"
expect(page).to have_content(issue_title)
.and have_content(user.name)
.and have_content(project.name)
.and have_content(label_titles.first)
fill_in("Title", with: issue_title)
click_button _('Select label')
wait_for_all_requests
page.within '[data-testid="sidebar-labels"]' do
click_button label_titles.first
click_button _('Close')
wait_for_requests
end
click_button("Create issue")
expect(page).to have_content(issue_title)
.and have_content(user.name)
.and have_content(project.name)
.and have_content(label_titles.first)
end
end
context 'with the visible_label_selection_on_metadata feature flag disabled' do
let(:visible_label_selection_on_metadata) { false }
it "creates issue" do
issue_title = "500 error on profile"
fill_in("Title", with: issue_title)
click_button("Label")
click_link(label_titles.first)
click_button("Create issue")
expect(page).to have_content(issue_title)
.and have_content(user.name)
.and have_content(project.name)
.and have_content(label_titles.first)
end
end
end

View File

@ -157,28 +157,71 @@ RSpec.describe 'Labels Hierarchy', :js, feature_category: :team_planning do
end
end
context 'when creating new issuable' do
context 'with the visible_label_selection_on_metadata feature flag enabled' do
before do
visit new_project_issue_path(project_1)
stub_feature_flags(visible_label_selection_on_metadata: true)
end
it 'is able to assign ancestor group labels' do
fill_in 'issue_title', with: 'new created issue'
fill_in 'issue_description', with: 'new issue description'
context 'when creating new issuable' do
before do
visit new_project_issue_path(project_1)
end
find(".js-label-select").click
wait_for_requests
it 'is able to assign ancestor group labels' do
fill_in 'issue_title', with: 'new created issue'
fill_in 'issue_description', with: 'new issue description'
find('a.label-item', text: grandparent_group_label.title).click
find('a.label-item', text: parent_group_label.title).click
find('a.label-item', text: project_label_1.title).click
click_button _('Select label')
find('.btn-confirm').click
wait_for_all_requests
expect(page.find('.issue-details h1.title')).to have_content('new created issue')
expect(page).to have_selector('span.gl-label-text', text: grandparent_group_label.title)
expect(page).to have_selector('span.gl-label-text', text: parent_group_label.title)
expect(page).to have_selector('span.gl-label-text', text: project_label_1.title)
page.within '[data-testid="sidebar-labels"]' do
click_button grandparent_group_label.title
click_button parent_group_label.title
click_button project_label_1.title
click_button _('Close')
wait_for_requests
end
find('.btn-confirm').click
expect(page.find('.issue-details h1.title')).to have_content('new created issue')
expect(page).to have_selector('span.gl-label-text', text: grandparent_group_label.title)
expect(page).to have_selector('span.gl-label-text', text: parent_group_label.title)
expect(page).to have_selector('span.gl-label-text', text: project_label_1.title)
end
end
end
context 'with the visible_label_selection_on_metadata feature flag disabled' do
before do
stub_feature_flags(visible_label_selection_on_metadata: false)
end
context 'when creating new issuable' do
before do
visit new_project_issue_path(project_1)
end
it 'is able to assign ancestor group labels' do
fill_in 'issue_title', with: 'new created issue'
fill_in 'issue_description', with: 'new issue description'
find(".js-label-select").click
wait_for_requests
find('a.label-item', text: grandparent_group_label.title).click
find('a.label-item', text: parent_group_label.title).click
find('a.label-item', text: project_label_1.title).click
find('.btn-confirm').click
expect(page.find('.issue-details h1.title')).to have_content('new created issue')
expect(page).to have_selector('span.gl-label-text', text: grandparent_group_label.title)
expect(page).to have_selector('span.gl-label-text', text: parent_group_label.title)
expect(page).to have_selector('span.gl-label-text', text: project_label_1.title)
end
end
end

View File

@ -9,35 +9,158 @@ RSpec.describe 'Merge request > User creates MR', feature_category: :code_review
stub_licensed_features(multiple_merge_request_assignees: false)
end
context 'non-fork merge request' do
include_context 'merge request create context'
it_behaves_like 'a creatable merge request'
shared_examples 'a creatable merge request with visible selected labels' do
include WaitForRequests
include ListboxHelpers
it 'creates new merge request', :js do
find('[data-testid="assignee-ids-dropdown-toggle"]').click
page.within '.dropdown-menu-user' do
click_link user2.name
end
expect(find('input[name="merge_request[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s)
page.within '[data-testid="assignee-ids-dropdown-toggle"]' do
expect(page).to have_content user2.name
end
click_link 'Assign to me'
expect(find('input[name="merge_request[assignee_ids][]"]', visible: false).value).to match(user.id.to_s)
page.within '[data-testid="assignee-ids-dropdown-toggle"]' do
expect(page).to have_content user.name
end
click_button 'Select milestone'
click_button milestone.title
expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
expect(page).to have_button milestone.title
click_button _('Select label')
wait_for_all_requests
page.within '[data-testid="sidebar-labels"]' do
click_button label.title
click_button label2.title
click_button _('Close')
wait_for_requests
page.within('[data-testid="embedded-labels-list"]') do
expect(page).to have_content(label.title)
expect(page).to have_content(label2.title)
end
end
click_button 'Create merge request'
page.within '.issuable-sidebar' do
page.within '.assignee' do
expect(page).to have_content user.name
end
page.within '.milestone' do
expect(page).to have_content milestone.title
end
page.within '.labels' do
expect(page).to have_content label.title
expect(page).to have_content label2.title
end
end
end
it 'updates the branches when selecting a new target project', :js do
target_project_member = target_project.first_owner
::Branches::CreateService.new(target_project, target_project_member)
.execute('a-brand-new-branch-to-test', 'master')
visit project_new_merge_request_path(source_project)
first('.js-target-project').click
select_listbox_item(target_project.full_path)
wait_for_requests
first('.js-target-branch').click
find('.gl-listbox-search-input').set('a-brand-new-branch-to-test')
wait_for_requests
expect_listbox_item('a-brand-new-branch-to-test')
end
end
context 'from a forked project' do
let(:canonical_project) { create(:project, :public, :repository) }
let(:source_project) do
fork_project(canonical_project, user,
repository: true,
namespace: user.namespace)
context 'with the visible_label_selection_on_metadata feature flag enabled' do
before do
stub_feature_flags(visible_label_selection_on_metadata: true)
end
context 'to canonical project' do
context 'non-fork merge request' do
include_context 'merge request create context'
it_behaves_like 'a creatable merge request'
it_behaves_like 'a creatable merge request with visible selected labels'
end
context 'to another forked project' do
let(:target_project) do
context 'from a forked project' do
let(:canonical_project) { create(:project, :public, :repository) }
let(:source_project) do
fork_project(canonical_project, user,
repository: true,
namespace: user.namespace)
end
context 'to canonical project' do
include_context 'merge request create context'
it_behaves_like 'a creatable merge request with visible selected labels'
end
context 'to another forked project' do
let(:target_project) do
fork_project(canonical_project, user,
repository: true,
namespace: user.namespace)
end
include_context 'merge request create context'
it_behaves_like 'a creatable merge request with visible selected labels'
end
end
end
context 'with the visible_label_selection_on_metadata feature flag disabled' do
before do
stub_feature_flags(visible_label_selection_on_metadata: false)
end
context 'non-fork merge request' do
include_context 'merge request create context'
it_behaves_like 'a creatable merge request'
end
context 'from a forked project' do
let(:canonical_project) { create(:project, :public, :repository) }
let(:source_project) do
fork_project(canonical_project, user,
repository: true,
namespace: user.namespace)
end
context 'to canonical project' do
include_context 'merge request create context'
it_behaves_like 'a creatable merge request'
end
context 'to another forked project' do
let(:target_project) do
fork_project(canonical_project, user,
repository: true,
namespace: user.namespace)
end
include_context 'merge request create context'
it_behaves_like 'a creatable merge request'
end
end
end
context 'source project', :js do

View File

@ -5,19 +5,227 @@ require 'spec_helper'
RSpec.describe 'Merge request > User edits MR', feature_category: :code_review_workflow do
include ProjectForksHelper
shared_examples 'an editable merge request with visible selected labels' do
it 'updates merge request', :js do
find('.js-assignee-search').click
page.within '.dropdown-menu-user' do
click_link user.name
end
expect(find('input[name="merge_request[assignee_ids][]"]', visible: false).value).to match(user.id.to_s)
page.within '.js-assignee-search' do
expect(page).to have_content user.name
end
find('.js-reviewer-search').click
page.within '.dropdown-menu-user' do
click_link user.name
end
expect(find('input[name="merge_request[reviewer_ids][]"]', visible: false).value).to match(user.id.to_s)
page.within '.js-reviewer-search' do
expect(page).to have_content user.name
end
click_button 'Select milestone'
click_button milestone.title
expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
expect(page).to have_button milestone.title
click_button _('Select label')
wait_for_all_requests
page.within '[data-testid="sidebar-labels"]' do
click_button label.title
click_button label2.title
click_button _('Close')
wait_for_requests
page.within('[data-testid="embedded-labels-list"]') do
expect(page).to have_content(label.title)
expect(page).to have_content(label2.title)
end
end
click_button 'Save changes'
page.within '.issuable-sidebar' do
page.within '.assignee' do
expect(page).to have_content user.name
end
page.within '.reviewer' do
expect(page).to have_content user.name
end
page.within '.milestone' do
expect(page).to have_content milestone.title
end
page.within '.labels' do
expect(page).to have_content label.title
expect(page).to have_content label2.title
end
end
end
it 'description has autocomplete', :js do
find('#merge_request_description').native.send_keys('')
fill_in 'merge_request_description', with: user.to_reference[0..4]
page.within('.atwho-view') do
expect(page).to have_content(user2.name)
end
end
it 'description has quick action autocomplete', :js do
find('#merge_request_description').native.send_keys('/')
expect(page).to have_selector('.atwho-container')
end
it 'has class js-quick-submit in form' do
expect(page).to have_selector('.js-quick-submit')
end
it 'warns about version conflict', :js do
merge_request.update!(title: "New title")
fill_in 'merge_request_title', with: 'bug 345'
fill_in 'merge_request_description', with: 'bug description'
click_button 'Save changes'
expect(page).to have_content 'Someone edited the merge request the same time you did'
end
it 'preserves description textarea height', :js do
long_description = %q(
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Etiam ac ornare ligula, ut tempus arcu.
Etiam ultricies accumsan dolor vitae faucibus.
Donec at elit lacus.
Mauris orci ante, aliquam quis lorem eget, convallis faucibus arcu.
Aenean at pulvinar lacus.
Ut viverra quam massa, molestie ornare tortor dignissim a.
Suspendisse tristique pellentesque tellus, id lacinia metus elementum id.
Nam tristique, arcu rhoncus faucibus viverra, lacus ipsum sagittis ligula, vitae convallis odio lacus a nibh.
Ut tincidunt est purus, ac vestibulum augue maximus in.
Suspendisse vel erat et mi ultricies semper.
Pellentesque volutpat pellentesque consequat.
Cras congue nec ligula tristique viverra.
Curabitur fringilla fringilla fringilla.
Donec rhoncus dignissim orci ut accumsan.
Ut rutrum urna a rhoncus varius.
Maecenas blandit, mauris nec accumsan gravida, augue nibh finibus magna, sed maximus turpis libero nec neque
Suspendisse at semper est.
Nunc imperdiet dapibus dui, varius sollicitudin erat luctus non.
Sed pellentesque ligula eget posuere facilisis.
Donec dictum commodo volutpat.
Donec egestas dui ac magna sollicitudin bibendum.
Vivamus purus neque, ullamcorper ac feugiat et, tempus sit amet metus.
Praesent quis viverra neque.
Sed bibendum viverra est, eu aliquam mi ornare vitae.
Proin et dapibus ipsum.
Nunc tortor diam, malesuada nec interdum vel, placerat quis justo.
Ut viverra at erat eu laoreet.
Pellentesque commodo, diam sit amet dignissim condimentum, tortor justo pretium est,
non venenatis metus eros ut nunc.
Etiam ut neque eget sem dapibus aliquam.
Curabitur vel elit lorem.
Nulla nec enim elit.
Sed ut ex id justo facilisis convallis at ac augue.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
Nullam cursus egestas turpis non tristique.
Suspendisse in erat sem.
Fusce libero elit, fermentum gravida mauris id, auctor iaculis felis.
Nullam vulputate tempor laoreet.
Nam tempor et magna sed convallis.
Fusce sit amet sollicitudin risus, a ullamcorper lacus.
Morbi gravida quis sem eget porttitor.
Donec eu egestas mauris, in elementum tortor.
Sed eget ex mi.
Mauris iaculis tortor ut est auctor, nec dignissim quam sagittis.
Suspendisse vel metus non quam suscipit tincidunt.
Cras molestie lacus non justo finibus sodales quis vitae erat.
In a porttitor nisi, id sollicitudin urna.
Ut at felis tellus.
Suspendisse potenti.
Maecenas leo ligula, varius at neque vitae, ornare maximus justo.
Nullam convallis luctus risus et vulputate.
Duis suscipit faucibus iaculis.
Etiam quis tortor faucibus, tristique tellus sit amet, sodales neque.
Nulla dapibus nisi vel aliquet consequat.
Etiam faucibus, metus eget condimentum iaculis, enim urna lobortis sem, id efficitur eros sapien nec nisi.
Aenean ut finibus ex.
)
fill_in 'merge_request_description', with: long_description
height = get_textarea_height
click_button("Preview")
click_button("Continue editing")
new_height = get_textarea_height
expect(height).to eq(new_height)
end
context 'when "Remove source branch" is set' do
before do
merge_request.update!(merge_params: { 'force_remove_source_branch' => '1' })
end
it 'allows to unselect "Remove source branch"', :js do
expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy
visit edit_project_merge_request_path(target_project, merge_request)
uncheck 'Delete source branch when merge request is accepted'
click_button 'Save changes'
expect(page).to have_unchecked_field 'remove-source-branch-input'
expect(page).to have_content 'Delete source branch'
end
end
end
before do
stub_licensed_features(multiple_merge_request_assignees: false)
end
context 'non-fork merge request' do
include_context 'merge request edit context'
it_behaves_like 'an editable merge request'
context 'with the visible_label_selection_on_metadata feature flag enabled' do
before do
stub_feature_flags(visible_label_selection_on_metadata: true)
end
context 'non-fork merge request' do
include_context 'merge request edit context'
it_behaves_like 'an editable merge request with visible selected labels'
end
context 'for a forked project' do
let(:source_project) { fork_project(target_project, nil, repository: true) }
include_context 'merge request edit context'
it_behaves_like 'an editable merge request with visible selected labels'
end
end
context 'for a forked project' do
let(:source_project) { fork_project(target_project, nil, repository: true) }
context 'with the visible_label_selection_on_metadata feature flag disabled' do
before do
stub_feature_flags(visible_label_selection_on_metadata: false)
end
include_context 'merge request edit context'
it_behaves_like 'an editable merge request'
context 'non-fork merge request' do
include_context 'merge request edit context'
it_behaves_like 'an editable merge request'
end
context 'for a forked project' do
let(:source_project) { fork_project(target_project, nil, repository: true) }
include_context 'merge request edit context'
it_behaves_like 'an editable merge request'
end
end
end

View File

@ -1,5 +1,4 @@
import { shallowMount } from '@vue/test-utils';
import { GlIcon } from '@gitlab/ui';
import {
mockRegularLabel,
mockScopedLabel,
@ -24,8 +23,6 @@ const workspaceType = WORKSPACE_PROJECT;
describe('IssuableLabelSelector', () => {
let wrapper;
const findTitle = () => wrapper.find('label').text().replace(/\s+/, ' ');
const findLabelIcon = () => wrapper.findComponent(GlIcon);
const findAllHiddenInputs = () => wrapper.findAll('input[type="hidden"]');
const findLabelSelector = () => wrapper.findComponent(LabelsSelect);
@ -47,23 +44,11 @@ describe('IssuableLabelSelector', () => {
});
};
const expectTitleWithCount = (count) => {
const title = findTitle();
expect(title).toContain(__('Labels'));
expect(title).toContain(count.toString());
};
describe('by default', () => {
beforeEach(() => {
wrapper = createComponent();
});
it('has the selected labels count', () => {
expectTitleWithCount(0);
expect(findLabelIcon().props('name')).toBe('labels');
});
it('has the label selector', () => {
expect(findLabelSelector().props()).toMatchObject({
allowLabelRemove,
@ -89,7 +74,6 @@ describe('IssuableLabelSelector', () => {
it('passing initial labels applies them to the form', () => {
wrapper = createComponent({ initialLabels: [mockRegularLabel, mockScopedLabel] });
expectTitleWithCount(2);
expect(findLabelSelector().props('selectedLabels')).toStrictEqual([
mockRegularLabel,
mockScopedLabel,
@ -103,13 +87,11 @@ describe('IssuableLabelSelector', () => {
it('updates the selected labels on the `updateSelectedLabels` event', async () => {
wrapper = createComponent();
expectTitleWithCount(0);
expect(findLabelSelector().props('selectedLabels')).toStrictEqual([]);
expect(findAllHiddenInputs()).toHaveLength(0);
await findLabelSelector().vm.$emit('updateSelectedLabels', { labels: [mockRegularLabel] });
expectTitleWithCount(1);
expect(findLabelSelector().props('selectedLabels')).toStrictEqual([mockRegularLabel]);
expect(findAllHiddenInputs().wrappers.map((input) => input.element.value)).toStrictEqual([
`${mockRegularLabel.id}`,
@ -119,7 +101,6 @@ describe('IssuableLabelSelector', () => {
it('updates the selected labels on the `onLabelRemove` event', async () => {
wrapper = createComponent({ initialLabels: [mockRegularLabel] });
expectTitleWithCount(1);
expect(findLabelSelector().props('selectedLabels')).toStrictEqual([mockRegularLabel]);
expect(findAllHiddenInputs().wrappers.map((input) => input.element.value)).toStrictEqual([
`${mockRegularLabel.id}`,
@ -127,7 +108,6 @@ describe('IssuableLabelSelector', () => {
await findLabelSelector().vm.$emit('onLabelRemove', mockRegularLabel.id);
expectTitleWithCount(0);
expect(findLabelSelector().props('selectedLabels')).toStrictEqual([]);
expect(findAllHiddenInputs()).toHaveLength(0);
});

View File

@ -728,4 +728,61 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
end
end
end
describe '#issuable_label_selector_data' do
let_it_be(:project) { create(:project, :repository) }
context 'with a new issuable' do
let_it_be(:issuable) { build(:issue, project: project) }
it 'returns the expected data' do
expect(helper.issuable_label_selector_data(project, issuable)).to match({
field_name: "#{issuable.class.model_name.param_key}[label_ids][]",
full_path: project.full_path,
initial_labels: '[]',
issuable_type: issuable.issuable_type,
labels_filter_base_path: project_issues_path(project),
labels_manage_path: project_labels_path(project)
})
end
end
context 'with an existing issuable' do
let_it_be(:label) { create(:label, name: 'Bug') }
let_it_be(:label2) { create(:label, name: 'Community contribution') }
let_it_be(:issuable) do
create(:merge_request, source_project: project, target_project: project, labels: [label, label2])
end
it 'returns the expected data' do
initial_labels = [
{
__typename: "Label",
id: label.id,
title: label.title,
description: label.description,
color: label.color,
text_color: label.text_color
},
{
__typename: "Label",
id: label2.id,
title: label2.title,
description: label2.description,
color: label2.color,
text_color: label2.text_color
}
]
expect(helper.issuable_label_selector_data(project, issuable)).to match({
field_name: "#{issuable.class.model_name.param_key}[label_ids][]",
full_path: project.full_path,
initial_labels: initial_labels.to_json,
issuable_type: issuable.issuable_type,
labels_filter_base_path: project_merge_requests_path(project),
labels_manage_path: project_labels_path(project)
})
end
end
end
end

View File

@ -19,7 +19,7 @@ RSpec.describe API::Entities::PersonalAccessToken do
user_id: user.id,
last_used_at: nil,
active: true,
expires_at: nil
expires_at: token.expires_at.iso8601
})
end
end

View File

@ -0,0 +1,46 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe RemoveShimoZentaoIntegrationRecords, feature_category: :integrations do
let(:integrations) { table(:integrations) }
let(:zentao_tracker_data) { table(:zentao_tracker_data) }
before do
integrations.create!(id: 1, type_new: 'Integrations::MockMonitoring')
integrations.create!(id: 2, type_new: 'Integrations::Redmine')
integrations.create!(id: 3, type_new: 'Integrations::Confluence')
integrations.create!(id: 4, type_new: 'Integrations::Shimo')
integrations.create!(id: 5, type_new: 'Integrations::Zentao')
integrations.create!(id: 6, type_new: 'Integrations::Zentao')
zentao_tracker_data.create!(id: 1, integration_id: 5)
zentao_tracker_data.create!(id: 2, integration_id: 6)
end
context 'with CE/EE env' do
it 'destroys all shimo and zentao integrations' do
migrate!
expect(integrations.count).to eq(3) # keep other integrations
expect(integrations.where(type_new: described_class::TYPES).count).to eq(0)
expect(zentao_tracker_data.count).to eq(0)
end
end
context 'with JiHu env' do
before do
allow(Gitlab).to receive(:jh?).and_return(true)
end
it 'keeps shimo and zentao integrations' do
migrate!
expect(integrations.count).to eq(6)
expect(integrations.where(type_new: described_class::TYPES).count).to eq(3)
expect(zentao_tracker_data.count).to eq(2)
end
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe BackfillCorrectedSecureFilesExpirations, migration: :gitlab_ci, feature_category: :mobile_devops do
let(:migration) { described_class.new }
let(:ci_secure_files) { table(:ci_secure_files) }
let!(:file1) { ci_secure_files.create!(project_id: 1, name: "file.cer", file: "foo", checksum: 'bar') }
let!(:file2) { ci_secure_files.create!(project_id: 1, name: "file.p12", file: "foo", checksum: 'bar') }
let!(:file3) { ci_secure_files.create!(project_id: 1, name: "file.jks", file: "foo", checksum: 'bar') }
describe '#up' do
it 'enqueues the ParseSecureFileMetadataWorker job for relevant file types', :aggregate_failures do
expect(::Ci::ParseSecureFileMetadataWorker).to receive(:perform_async).with(file1.id)
expect(::Ci::ParseSecureFileMetadataWorker).to receive(:perform_async).with(file2.id)
expect(::Ci::ParseSecureFileMetadataWorker).not_to receive(:perform_async).with(file3.id)
migration.up
end
end
end

View File

@ -976,7 +976,7 @@ RSpec.describe Issuable do
end
end
describe '#incident?' do
describe '#incident_type_issue?' do
where(:issuable_type, :incident) do
:issue | false
:incident | true
@ -986,7 +986,7 @@ RSpec.describe Issuable do
with_them do
let(:issuable) { build_stubbed(issuable_type) }
subject { issuable.incident? }
subject { issuable.incident_type_issue? }
it { is_expected.to eq(incident) }
end

View File

@ -2001,4 +2001,18 @@ RSpec.describe Issue, feature_category: :team_planning do
it { is_expected.to eq(WorkItems::Type.default_by_type(::Issue::DEFAULT_ISSUE_TYPE)) }
end
describe 'issue_type enum generated methods' do
using RSpec::Parameterized::TableSyntax
let_it_be(:issue) { create(:issue, project: reusable_project) }
where(issue_type: WorkItems::Type.base_types.keys)
with_them do
it 'raises an error if called' do
expect { issue.public_send("#{issue_type}?".to_sym) }.to raise_error(Issue::ForbiddenColumnUsed)
end
end
end
end

View File

@ -267,6 +267,41 @@ RSpec.describe PersonalAccessToken, feature_category: :system_access do
expect(personal_access_token).not_to be_valid
expect(personal_access_token.errors[:scopes].first).to eq "can only contain available scopes"
end
context 'validates expires_at' do
let(:max_expiration_date) { described_class::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now }
context 'when default_pat_expiration feature flag is true' do
context 'when expires_in is less than MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS days' do
it 'is valid' do
personal_access_token.expires_at = max_expiration_date - 1.day
expect(personal_access_token).to be_valid
end
end
context 'when expires_in is more than MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS days' do
it 'is invalid' do
personal_access_token.expires_at = max_expiration_date + 1.day
expect(personal_access_token).not_to be_valid
expect(personal_access_token.errors[:expires_at].first).to eq('must expire in 365 days')
end
end
end
context 'when default_pat_expiration feature flag is false' do
before do
stub_feature_flags(default_pat_expiration: false)
end
it 'allows any expires_at value' do
personal_access_token.expires_at = max_expiration_date + 1.day
expect(personal_access_token).to be_valid
end
end
end
end
describe 'scopes' do
@ -289,7 +324,7 @@ RSpec.describe PersonalAccessToken, feature_category: :system_access do
let_it_be(:revoked_token) { create(:personal_access_token, revoked: true) }
let_it_be(:valid_token_and_notified) { create(:personal_access_token, expires_at: 2.days.from_now, expire_notification_delivered: true) }
let_it_be(:valid_token) { create(:personal_access_token, expires_at: 2.days.from_now) }
let_it_be(:long_expiry_token) { create(:personal_access_token, expires_at: '999999-12-31'.to_date) }
let_it_be(:long_expiry_token) { create(:personal_access_token, expires_at: described_class::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now) }
context 'in one day' do
it "doesn't have any tokens" do
@ -427,4 +462,36 @@ RSpec.describe PersonalAccessToken, feature_category: :system_access do
end
end
end
describe '#expires_at=' do
let(:personal_access_token) { described_class.new }
context 'when default_pat_expiration feature flag is true' do
context 'expires_at set to empty value' do
[nil, ""].each do |expires_in_value|
it 'defaults to PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS' do
personal_access_token.expires_at = expires_in_value
freeze_time do
expect(personal_access_token.expires_at).to eq(
PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now.to_date
)
end
end
end
end
end
context 'when default_pat_expiration feature flag is false' do
before do
stub_feature_flags(default_pat_expiration: false)
end
it 'does not set a default' do
personal_access_token.expires_at = nil
expect(personal_access_token.expires_at).to eq(nil)
end
end
end
end

View File

@ -4,4 +4,5 @@ require 'spec_helper'
RSpec.describe ProtectedBranch::MergeAccessLevel, feature_category: :source_code_management do
include_examples 'protected branch access'
include_examples 'protected ref access allowed_access_levels'
end

View File

@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe ProtectedBranch::PushAccessLevel, feature_category: :source_code_management do
include_examples 'protected branch access'
include_examples 'protected ref access allowed_access_levels'
describe 'associations' do
it { is_expected.to belong_to(:deploy_key) }

View File

@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe ProtectedTag::CreateAccessLevel, feature_category: :source_code_management do
include_examples 'protected tag access'
include_examples 'protected ref access allowed_access_levels'
describe 'associations' do
it { is_expected.to belong_to(:deploy_key) }

View File

@ -8,8 +8,8 @@ RSpec.describe 'UserAchievements', feature_category: :user_profile do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) }
let_it_be(:achievement) { create(:achievement, namespace: group) }
let_it_be(:user_achievement1) { create(:user_achievement, achievement: achievement, user: user) }
let_it_be(:user_achievement2) { create(:user_achievement, :revoked, achievement: achievement, user: user) }
let_it_be(:non_revoked_achievement1) { create(:user_achievement, achievement: achievement, user: user) }
let_it_be(:non_revoked_achievement2) { create(:user_achievement, :revoked, achievement: achievement, user: user) }
let_it_be(:fields) do
<<~HEREDOC
id
@ -51,11 +51,10 @@ RSpec.describe 'UserAchievements', feature_category: :user_profile do
it_behaves_like 'a working graphql query'
it 'returns all user_achievements' do
it 'returns all non_revoked user_achievements' do
expect(graphql_data_at(:namespace, :achievements, :nodes, :userAchievements, :nodes))
.to contain_exactly(
a_graphql_entity_for(user_achievement1),
a_graphql_entity_for(user_achievement2)
a_graphql_entity_for(non_revoked_achievement1)
)
end
@ -65,9 +64,7 @@ RSpec.describe 'UserAchievements', feature_category: :user_profile do
end.count
user2 = create(:user)
create_list(:achievement, 3, namespace: group) do |a|
create(:user_achievement, achievement: a, user: user2)
end
create(:user_achievement, achievement: achievement, user: user2)
expect { post_graphql(query, current_user: user) }.not_to exceed_all_query_limit(control_count)
end

View File

@ -8,7 +8,8 @@ RSpec.describe 'UserAchievements', feature_category: :user_profile do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :private) }
let_it_be(:achievement) { create(:achievement, namespace: group) }
let_it_be(:user_achievements) { create_list(:user_achievement, 2, achievement: achievement, user: user) }
let_it_be(:non_revoked_achievement) { create(:user_achievement, achievement: achievement, user: user) }
let_it_be(:revoked_achievement) { create(:user_achievement, :revoked, achievement: achievement, user: user) }
let_it_be(:fields) do
<<~HEREDOC
userAchievements {
@ -47,10 +48,9 @@ RSpec.describe 'UserAchievements', feature_category: :user_profile do
it_behaves_like 'a working graphql query'
it 'returns all user_achievements' do
it 'returns all non_revoked user_achievements' do
expect(graphql_data_at(:user, :userAchievements, :nodes)).to contain_exactly(
a_graphql_entity_for(user_achievements[0]),
a_graphql_entity_for(user_achievements[1])
a_graphql_entity_for(non_revoked_achievement)
)
end
@ -88,8 +88,7 @@ RSpec.describe 'UserAchievements', feature_category: :user_profile do
it 'returns all achievements' do
expect(graphql_data_at(:user, :userAchievements, :nodes)).to contain_exactly(
a_graphql_entity_for(user_achievements[0]),
a_graphql_entity_for(user_achievements[1])
a_graphql_entity_for(non_revoked_achievement)
)
end
end

View File

@ -10,6 +10,9 @@ RSpec.describe API::Internal::Base, feature_category: :system_access do
let_it_be(:project, reload: true) { create(:project, :repository, :wiki_repo) }
let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: user) }
let_it_be(:project_snippet) { create(:project_snippet, :repository, author: user, project: project) }
let_it_be(:max_pat_access_token_lifetime) do
PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now.to_date.freeze
end
let(:key) { create(:key, user: user) }
let(:secret_token) { Gitlab::Shell.secret_token }
@ -194,39 +197,68 @@ RSpec.describe API::Internal::Base, feature_category: :system_access do
expect(json_response['message']).to match(/\AInvalid scope: 'badscope'. Valid scopes are: /)
end
it 'returns a token without expiry when the expires_at parameter is missing' do
token_size = (PersonalAccessToken.token_prefix || '').size + 20
it 'returns a token with expiry when it receives a valid expires_at parameter' do
freeze_time do
token_size = (PersonalAccessToken.token_prefix || '').size + 20
post api('/internal/personal_access_token'),
params: {
key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository)
},
headers: gitlab_shell_internal_api_request_header
post api('/internal/personal_access_token'),
params: {
key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository),
expires_at: max_pat_access_token_lifetime
},
headers: gitlab_shell_internal_api_request_header
expect(json_response['success']).to be_truthy
expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
expect(json_response['scopes']).to match_array(%w(read_api read_repository))
expect(json_response['expires_at']).to be_nil
expect(json_response['success']).to be_truthy
expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
expect(json_response['scopes']).to match_array(%w(read_api read_repository))
expect(json_response['expires_at']).to eq(max_pat_access_token_lifetime.iso8601)
end
end
it 'returns a token with expiry when it receives a valid expires_at parameter' do
token_size = (PersonalAccessToken.token_prefix || '').size + 20
context 'when default_pat_expiration feature flag is true' do
it 'returns token with expiry as PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS' do
freeze_time do
token_size = (PersonalAccessToken.token_prefix || '').size + 20
post api('/internal/personal_access_token'),
params: {
key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository),
expires_at: '9001-11-17'
},
headers: gitlab_shell_internal_api_request_header
post api('/internal/personal_access_token'),
params: {
key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository)
},
headers: gitlab_shell_internal_api_request_header
expect(json_response['success']).to be_truthy
expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
expect(json_response['scopes']).to match_array(%w(read_api read_repository))
expect(json_response['expires_at']).to eq('9001-11-17')
expect(json_response['success']).to be_truthy
expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
expect(json_response['scopes']).to match_array(%w(read_api read_repository))
expect(json_response['expires_at']).to eq(max_pat_access_token_lifetime.iso8601)
end
end
end
context 'when default_pat_expiration feature flag is false' do
before do
stub_feature_flags(default_pat_expiration: false)
end
it 'uses nil expiration value' do
token_size = (PersonalAccessToken.token_prefix || '').size + 20
post api('/internal/personal_access_token'),
params: {
key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository)
},
headers: gitlab_shell_internal_api_request_header
expect(json_response['success']).to be_truthy
expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
expect(json_response['scopes']).to match_array(%w(read_api read_repository))
expect(json_response['expires_at']).to be_nil
end
end
end

View File

@ -1210,7 +1210,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
it 'allows issue type to be converted' do
put api("/projects/#{project.id}/issues/#{issue.iid}", user), params: { issue_type: 'incident' }
expect(issue.reload.incident?).to be(true)
expect(issue.reload.work_item_type.incident?).to be(true)
end
end
end

View File

@ -336,13 +336,33 @@ RSpec.describe API::ResourceAccessTokens, feature_category: :system_access do
context "when 'expires_at' is not set" do
let(:expires_at) { nil }
it "creates a #{source_type} access token with the params", :aggregate_failures do
create_token
context 'when default_pat_expiration feature flag is true' do
it "creates a #{source_type} access token with the default expires_at value", :aggregate_failures do
freeze_time do
create_token
expires_at = PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now
expect(response).to have_gitlab_http_status(:created)
expect(json_response["name"]).to eq("test")
expect(json_response["scopes"]).to eq(["api"])
expect(json_response["expires_at"]).to eq(nil)
expect(response).to have_gitlab_http_status(:created)
expect(json_response["name"]).to eq("test")
expect(json_response["scopes"]).to eq(["api"])
expect(json_response["expires_at"]).to eq(expires_at.to_date.iso8601)
end
end
end
context 'when default_pat_expiration feature flag is false' do
before do
stub_feature_flags(default_pat_expiration: false)
end
it "creates a #{source_type} access token with the params", :aggregate_failures do
create_token
expect(response).to have_gitlab_http_status(:created)
expect(json_response["name"]).to eq("test")
expect(json_response["scopes"]).to eq(["api"])
expect(json_response["expires_at"]).to eq(nil)
end
end
end

View File

@ -16,7 +16,7 @@ RSpec.describe AccessTokenEntityBase do
revoked: false,
created_at: token.created_at,
scopes: token.scopes,
expires_at: nil,
expires_at: token.expires_at.iso8601,
expired: false,
expires_soon: false
))

View File

@ -157,7 +157,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
context 'when a build_service is provided' do
let(:result) { described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params, build_service: build_service).execute }
let(:issue_from_builder) { WorkItem.new(project: project, title: 'Issue from builder') }
let(:issue_from_builder) { build(:work_item, project: project, title: 'Issue from builder') }
let(:build_service) { double(:build_service, execute: issue_from_builder) }
it 'uses the provided service to build the issue' do

View File

@ -9,6 +9,9 @@ RSpec.describe ResourceAccessTokens::CreateService, feature_category: :system_ac
let_it_be(:project) { create(:project, :private) }
let_it_be(:group) { create(:group, :private) }
let_it_be(:params) { {} }
let_it_be(:max_pat_access_token_lifetime) do
PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now.to_date.freeze
end
before do
stub_config_setting(host: 'example.com')
@ -185,20 +188,51 @@ RSpec.describe ResourceAccessTokens::CreateService, feature_category: :system_ac
context 'expires_at' do
context 'when no expiration value is passed' do
it 'uses nil expiration value' do
response = subject
access_token = response.payload[:access_token]
context 'when default_pat_expiration feature flag is true' do
it 'defaults to PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS' do
freeze_time do
response = subject
access_token = response.payload[:access_token]
expect(access_token.expires_at).to eq(nil)
expect(access_token.expires_at).to eq(
max_pat_access_token_lifetime.to_date
)
end
end
context 'expiry of the project bot member' do
it 'project bot membership does not expire' do
response = subject
access_token = response.payload[:access_token]
project_bot = access_token.user
expect(resource.members.find_by(user_id: project_bot.id).expires_at).to eq(
max_pat_access_token_lifetime.to_date
)
end
end
end
context 'expiry of the project bot member' do
it 'project bot membership does not expire' do
context 'when default_pat_expiration feature flag is false' do
before do
stub_feature_flags(default_pat_expiration: false)
end
it 'uses nil expiration value' do
response = subject
access_token = response.payload[:access_token]
project_bot = access_token.user
expect(resource.members.find_by(user_id: project_bot.id).expires_at).to eq(nil)
expect(access_token.expires_at).to eq(nil)
end
context 'expiry of the project bot member' do
it 'project bot membership expires' do
response = subject
access_token = response.payload[:access_token]
project_bot = access_token.user
expect(resource.members.find_by(user_id: project_bot.id).expires_at).to eq(nil)
end
end
end
end
@ -219,7 +253,7 @@ RSpec.describe ResourceAccessTokens::CreateService, feature_category: :system_ac
access_token = response.payload[:access_token]
project_bot = access_token.user
expect(resource.members.find_by(user_id: project_bot.id).expires_at).to eq(params[:expires_at])
expect(resource.members.find_by(user_id: project_bot.id).expires_at).to eq(access_token.expires_at)
end
end
end

View File

@ -5,20 +5,20 @@ RSpec.shared_examples 'a creatable merge request' do
include ListboxHelpers
it 'creates new merge request', :js do
find('.js-assignee-search').click
find('[data-testid="assignee-ids-dropdown-toggle"]').click
page.within '.dropdown-menu-user' do
click_link user2.name
end
expect(find('input[name="merge_request[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s)
page.within '.js-assignee-search' do
page.within '[data-testid="assignee-ids-dropdown-toggle"]' do
expect(page).to have_content user2.name
end
click_link 'Assign to me'
expect(find('input[name="merge_request[assignee_ids][]"]', visible: false).value).to match(user.id.to_s)
page.within '.js-assignee-search' do
page.within '[data-testid="assignee-ids-dropdown-toggle"]' do
expect(page).to have_content user.name
end

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
RSpec.shared_examples 'protected ref access allowed_access_levels' do |excludes: []|
describe '::allowed_access_levels' do
subject { described_class.allowed_access_levels }
let(:all_levels) do
[
Gitlab::Access::DEVELOPER,
Gitlab::Access::MAINTAINER,
Gitlab::Access::ADMIN,
Gitlab::Access::NO_ACCESS
]
end
context 'when running on Gitlab.com?' do
let(:levels) { all_levels.excluding(Gitlab::Access::ADMIN, *excludes) }
before do
allow(Gitlab).to receive(:com?).and_return(true)
end
it { is_expected.to match_array(levels) }
end
context 'when self hosted?' do
let(:levels) { all_levels.excluding(*excludes) }
before do
allow(Gitlab).to receive(:com?).and_return(false)
end
it { is_expected.to match_array(levels) }
end
end
end

View File

@ -18,6 +18,21 @@ RSpec.shared_examples 'protected ref access' do |association|
it { is_expected.not_to validate_presence_of(:access_level) }
end
describe '::human_access_levels' do
subject { described_class.human_access_levels }
let(:levels) do
{
Gitlab::Access::DEVELOPER => "Developers + Maintainers",
Gitlab::Access::MAINTAINER => "Maintainers",
Gitlab::Access::ADMIN => 'Instance admins',
Gitlab::Access::NO_ACCESS => "No one"
}.slice(*described_class.allowed_access_levels)
end
it { is_expected.to eq(levels) }
end
describe '#check_access' do
let_it_be(:current_user) { create(:user) }
@ -44,6 +59,22 @@ RSpec.shared_examples 'protected ref access' do |association|
it { expect(subject.check_access(current_user)).to eq(false) }
end
context 'when instance admin access is configured' do
let(:access_level) { Gitlab::Access::ADMIN }
context 'when current_user is a maintainer' do
it { expect(subject.check_access(current_user)).to eq(false) }
end
context 'when current_user is admin' do
before do
allow(current_user).to receive(:admin?).and_return(true)
end
it { expect(subject.check_access(current_user)).to eq(true) }
end
end
context 'when current_user can push_code to project' do
context 'and member access is high enough' do
it { expect(subject.check_access(current_user)).to eq(true) }

View File

@ -35,30 +35,76 @@ RSpec.describe 'projects/merge_requests/edit.html.haml' do
.and_return(User.find(closed_merge_request.author_id))
end
context 'when a merge request without fork' do
it "shows editable fields" do
unlink_project.execute
closed_merge_request.reload
shared_examples 'merge request shows editable fields' do
it 'shows editable fields' do
render
expect(rendered).to have_field('merge_request[title]')
expect(rendered).to have_selector('input[name="merge_request[description]"]', visible: false)
expect(rendered).to have_selector('input[name="merge_request[label_ids][]"]', visible: false)
expect(rendered).to have_selector('.js-milestone-dropdown-root')
expect(rendered).not_to have_selector('#merge_request_target_branch', visible: false)
end
end
context 'when a merge request with an existing source project is closed' do
it "shows editable fields" do
render
expect(rendered).to have_field('merge_request[title]')
expect(rendered).to have_selector('input[name="merge_request[description]"]', visible: false)
expect(rendered).to have_selector('input[name="merge_request[label_ids][]"]', visible: false)
expect(rendered).to have_selector('.js-milestone-dropdown-root')
expect(rendered).to have_selector('#merge_request_target_branch', visible: false)
end
end
context 'with the visible_label_selection_on_metadata feature flag enabled' do
before do
stub_feature_flags(visible_label_selection_on_metadata: true)
end
context 'when a merge request without fork' do
it_behaves_like 'merge request shows editable fields'
it "shows editable fields" do
unlink_project.execute
closed_merge_request.reload
render
expect(rendered).not_to have_selector('#merge_request_target_branch', visible: false)
expect(rendered).to have_selector('.js-issuable-form-label-selector')
end
end
context 'when a merge request with an existing source project is closed' do
it_behaves_like 'merge request shows editable fields'
it "shows editable fields" do
render
expect(rendered).to have_selector('#merge_request_target_branch', visible: false)
expect(rendered).to have_selector('.js-issuable-form-label-selector')
end
end
end
context 'with the visible_label_selection_on_metadata feature flag disabled' do
before do
stub_feature_flags(visible_label_selection_on_metadata: false)
end
context 'when a merge request without fork' do
it_behaves_like 'merge request shows editable fields'
it "shows editable fields" do
unlink_project.execute
closed_merge_request.reload
render
expect(rendered).not_to have_selector('#merge_request_target_branch', visible: false)
expect(rendered).not_to have_selector('.js-issuable-form-label-selector')
end
end
context 'when a merge request with an existing source project is closed' do
it_behaves_like 'merge request shows editable fields'
it "shows editable fields" do
render
expect(rendered).to have_selector('#merge_request_target_branch', visible: false)
expect(rendered).not_to have_selector('.js-issuable-form-label-selector')
end
end
end
end

View File

@ -4025,12 +4025,17 @@ commander@7, commander@^7.0.0, commander@^7.2.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
commander@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
commander@^6.0.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
commander@^9.4.0, commander@~9.4.0:
commander@~9.4.0:
version "9.4.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.0.tgz#bc4a40918fefe52e22450c111ecd6b7acce6f11c"
integrity sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==
@ -11191,10 +11196,10 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7:
version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.5.0:
version "7.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0"
integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==
dependencies:
lru-cache "^6.0.0"
@ -12228,7 +12233,7 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1:
tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
@ -13348,15 +13353,15 @@ yarn-check-webpack-plugin@^1.2.0:
dependencies:
chalk "^2.4.2"
yarn-deduplicate@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-6.0.0.tgz#91bc0b7b374efe24796606df2c6b00eabb5aab62"
integrity sha512-HjGVvuy10hetOuXeexXXT77V+6FfgS+NiW3FsmQD88yfF2kBqTpChvMglyKUlQ0xXEcI77VJazll5qKKBl3ssw==
yarn-deduplicate@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-6.0.2.tgz#63498d2d4c3a8567e992a994ce0ab51aa5681f2e"
integrity sha512-Efx4XEj82BgbRJe5gvQbZmEO7pU5DgHgxohYZp98/+GwPqdU90RXtzvHirb7hGlde0sQqk5G3J3Woyjai8hVqA==
dependencies:
"@yarnpkg/lockfile" "^1.1.0"
commander "^9.4.0"
semver "^7.3.7"
tslib "^2.4.0"
commander "^10.0.1"
semver "^7.5.0"
tslib "^2.5.0"
yocto-queue@^0.1.0:
version "0.1.0"