Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
4ea9f70a2f
commit
9cd5033338
|
@ -152,6 +152,12 @@ export default {
|
|||
isLoading() {
|
||||
return this.$apollo.queries.boardList.loading;
|
||||
},
|
||||
totalWeight() {
|
||||
return this.boardList?.totalWeight;
|
||||
},
|
||||
canShowTotalWeight() {
|
||||
return this.weightFeatureAvailable && !this.isLoading;
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
boardList: {
|
||||
|
@ -356,7 +362,7 @@ export default {
|
|||
<div v-if="weightFeatureAvailable && !isLoading">
|
||||
•
|
||||
<gl-sprintf :message="__('%{totalWeight} total weight')">
|
||||
<template #totalWeight>{{ boardList.totalWeight }}</template>
|
||||
<template #totalWeight>{{ totalWeight }}</template>
|
||||
</gl-sprintf>
|
||||
</div>
|
||||
</gl-tooltip>
|
||||
|
@ -381,11 +387,11 @@ export default {
|
|||
/>
|
||||
</span>
|
||||
<!-- EE start -->
|
||||
<template v-if="weightFeatureAvailable && !isEpicBoard && !isLoading">
|
||||
<template v-if="canShowTotalWeight">
|
||||
<gl-tooltip :target="() => $refs.weightTooltip" :title="weightCountToolTip" />
|
||||
<span ref="weightTooltip" class="gl-display-inline-flex gl-ml-3">
|
||||
<span ref="weightTooltip" class="gl-display-inline-flex gl-ml-3" data-testid="weight">
|
||||
<gl-icon class="gl-mr-2" name="weight" />
|
||||
{{ boardList.totalWeight }}
|
||||
{{ totalWeight }}
|
||||
</span>
|
||||
</template>
|
||||
<!-- EE end -->
|
||||
|
|
|
@ -106,7 +106,7 @@ export default {
|
|||
<div class="gl-text-right">
|
||||
<gl-button
|
||||
ref="clipboardCopyBtn"
|
||||
variant="success"
|
||||
variant="confirm"
|
||||
category="secondary"
|
||||
:data-clipboard-text="yml"
|
||||
class="gl-xs-w-full gl-xs-mb-3"
|
||||
|
@ -116,7 +116,7 @@ export default {
|
|||
</gl-button>
|
||||
<gl-button
|
||||
type="submit"
|
||||
variant="success"
|
||||
variant="confirm"
|
||||
:disabled="panelPreviewIsLoading"
|
||||
class="js-no-auto-disable gl-xs-w-full"
|
||||
>
|
||||
|
@ -170,7 +170,7 @@ export default {
|
|||
</gl-button>
|
||||
<gl-button
|
||||
ref="openRepositoryBtn"
|
||||
variant="success"
|
||||
variant="confirm"
|
||||
:href="projectPath"
|
||||
class="gl-xs-w-full"
|
||||
>
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
export default function initProjectLoadingSpinner() {
|
||||
const $formContainer = $('.project-edit-container');
|
||||
const $loadingSpinner = $('.save-project-loader');
|
||||
const formContainer = document.querySelector('.project-edit-container');
|
||||
if (formContainer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loadingSpinner = document.querySelector('.save-project-loader');
|
||||
|
||||
// show loading spinner when saving
|
||||
$formContainer.on('ajax:before', () => {
|
||||
$formContainer.hide();
|
||||
$loadingSpinner.show();
|
||||
formContainer.addEventListener('ajax:before', () => {
|
||||
formContainer.style.display = 'none';
|
||||
loadingSpinner.style.display = 'block';
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ const PERSISTENT_USER_CALLOUTS = [
|
|||
'.js-new-user-signups-cap-reached',
|
||||
'.js-eoa-bronze-plan-banner',
|
||||
'.js-security-newsletter-callout',
|
||||
'.js-approaching-seats-count-threshold',
|
||||
'.js-approaching-seat-count-threshold',
|
||||
'.js-storage-enforcement-banner',
|
||||
'.js-user-over-limit-free-plan-alert',
|
||||
'.js-minute-limit-banner',
|
||||
|
|
|
@ -58,7 +58,16 @@ module Registrations
|
|||
def path_for_signed_in_user(user)
|
||||
return users_almost_there_path(email: user.email) if requires_confirmation?(user)
|
||||
|
||||
stored_location_for(user) || members_activity_path(user.members)
|
||||
stored_url = stored_location_for(user)
|
||||
if ::Feature.enabled?(:about_your_company_registration_flow) &&
|
||||
stored_url&.include?(new_users_sign_up_company_path)
|
||||
company_params = update_params.slice(:role, :other_role, :registration_objective)
|
||||
.merge(params.permit(:jobs_to_be_done_other))
|
||||
redirect_uri = Gitlab::Utils.add_url_parameters(stored_url, company_params)
|
||||
store_location_for(:user, redirect_uri)
|
||||
else
|
||||
stored_url || members_activity_path(user.members)
|
||||
end
|
||||
end
|
||||
|
||||
def members_activity_path(members)
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Convert any ActiveRecord::Relation to a Gitlab::SQL::CTE
|
||||
module AsCte
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def as_cte(name, **opts)
|
||||
Gitlab::SQL::CTE.new(name, all, **opts)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,6 +5,8 @@ module Namespaces
|
|||
module LinearScopes
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include AsCte
|
||||
|
||||
class_methods do
|
||||
# When filtering namespaces by the traversal_ids column to compile a
|
||||
# list of namespace IDs, it can be faster to reference the ID in
|
||||
|
@ -25,25 +27,15 @@ module Namespaces
|
|||
def self_and_ancestors(include_self: true, upto: nil, hierarchy_order: nil)
|
||||
return super unless use_traversal_ids_for_ancestor_scopes?
|
||||
|
||||
ancestors_cte, base_cte = ancestor_ctes
|
||||
namespaces = Arel::Table.new(:namespaces)
|
||||
|
||||
records = unscoped
|
||||
.with(base_cte.to_arel, ancestors_cte.to_arel)
|
||||
.distinct
|
||||
.from([ancestors_cte.table, namespaces])
|
||||
.where(namespaces[:id].eq(ancestors_cte.table[:ancestor_id]))
|
||||
.order_by_depth(hierarchy_order)
|
||||
|
||||
unless include_self
|
||||
records = records.where(ancestors_cte.table[:base_id].not_eq(ancestors_cte.table[:ancestor_id]))
|
||||
if Feature.enabled?(:use_traversal_ids_for_ancestor_scopes_with_inner_join)
|
||||
self_and_ancestors_from_inner_join(include_self: include_self,
|
||||
upto: upto, hierarchy_order:
|
||||
hierarchy_order)
|
||||
else
|
||||
self_and_ancestors_from_ancestors_cte(include_self: include_self,
|
||||
upto: upto,
|
||||
hierarchy_order: hierarchy_order)
|
||||
end
|
||||
|
||||
if upto
|
||||
records = records.where.not(id: unscoped.where(id: upto).select('unnest(traversal_ids)'))
|
||||
end
|
||||
|
||||
records
|
||||
end
|
||||
|
||||
def self_and_ancestor_ids(include_self: true)
|
||||
|
@ -87,7 +79,7 @@ module Namespaces
|
|||
depth_order = hierarchy_order == :asc ? :desc : :asc
|
||||
|
||||
all
|
||||
.select(Arel.star, 'array_length(traversal_ids, 1) as depth')
|
||||
.select(Namespace.default_select_columns, 'array_length(traversal_ids, 1) as depth')
|
||||
.order(depth: depth_order, id: :asc)
|
||||
end
|
||||
|
||||
|
@ -125,10 +117,73 @@ module Namespaces
|
|||
use_traversal_ids?
|
||||
end
|
||||
|
||||
def self_and_ancestors_from_ancestors_cte(include_self: true, upto: nil, hierarchy_order: nil)
|
||||
base_cte = all.select('namespaces.id', 'namespaces.traversal_ids').as_cte(:base_ancestors_cte)
|
||||
|
||||
# We have to alias id with 'AS' to avoid ambiguous column references by calling methods.
|
||||
ancestors_cte = unscoped
|
||||
.unscope(where: [:type])
|
||||
.select('id as base_id',
|
||||
"#{unnest_func(base_cte.table['traversal_ids']).to_sql} as ancestor_id")
|
||||
.from(base_cte.table)
|
||||
.as_cte(:ancestors_cte)
|
||||
|
||||
namespaces = Arel::Table.new(:namespaces)
|
||||
|
||||
records = unscoped
|
||||
.with(base_cte.to_arel, ancestors_cte.to_arel)
|
||||
.distinct
|
||||
.from([ancestors_cte.table, namespaces])
|
||||
.where(namespaces[:id].eq(ancestors_cte.table[:ancestor_id]))
|
||||
.order_by_depth(hierarchy_order)
|
||||
|
||||
unless include_self
|
||||
records = records.where(ancestors_cte.table[:base_id].not_eq(ancestors_cte.table[:ancestor_id]))
|
||||
end
|
||||
|
||||
if upto
|
||||
records = records.where.not(id: unscoped.where(id: upto).select('unnest(traversal_ids)'))
|
||||
end
|
||||
|
||||
records
|
||||
end
|
||||
|
||||
def self_and_ancestors_from_inner_join(include_self: true, upto: nil, hierarchy_order: nil)
|
||||
base_cte = all.reselect('namespaces.traversal_ids').as_cte(:base_ancestors_cte)
|
||||
|
||||
unnest = if include_self
|
||||
base_cte.table[:traversal_ids]
|
||||
else
|
||||
base_cte_traversal_ids = 'base_ancestors_cte.traversal_ids'
|
||||
traversal_ids_range = "1:array_length(#{base_cte_traversal_ids},1)-1"
|
||||
Arel.sql("#{base_cte_traversal_ids}[#{traversal_ids_range}]")
|
||||
end
|
||||
|
||||
ancestor_subselect = "SELECT DISTINCT #{unnest_func(unnest).to_sql} FROM base_ancestors_cte"
|
||||
ancestors_join = <<~SQL
|
||||
INNER JOIN (#{ancestor_subselect}) AS ancestors(ancestor_id) ON namespaces.id = ancestors.ancestor_id
|
||||
SQL
|
||||
|
||||
namespaces = Arel::Table.new(:namespaces)
|
||||
|
||||
records = unscoped
|
||||
.with(base_cte.to_arel)
|
||||
.from(namespaces)
|
||||
.joins(ancestors_join)
|
||||
.order_by_depth(hierarchy_order)
|
||||
|
||||
if upto
|
||||
upto_ancestor_ids = unscoped.where(id: upto).select(unnest_func(Arel.sql('traversal_ids')))
|
||||
records = records.where.not(id: upto_ancestor_ids)
|
||||
end
|
||||
|
||||
records
|
||||
end
|
||||
|
||||
def self_and_descendants_with_comparison_operators(include_self: true)
|
||||
base = all.select(:traversal_ids)
|
||||
base = base.select(:id) if Feature.enabled?(:linear_scopes_superset)
|
||||
base_cte = Gitlab::SQL::CTE.new(:descendants_base_cte, base)
|
||||
base_cte = base.as_cte(:descendants_base_cte)
|
||||
|
||||
namespaces = Arel::Table.new(:namespaces)
|
||||
|
||||
|
@ -169,6 +224,10 @@ module Namespaces
|
|||
Arel::Nodes::NamedFunction.new('next_traversal_ids_sibling', args)
|
||||
end
|
||||
|
||||
def unnest_func(*args)
|
||||
Arel::Nodes::NamedFunction.new('unnest', args)
|
||||
end
|
||||
|
||||
def self_and_descendants_with_duplicates_with_array_operator(include_self: true)
|
||||
base_ids = select(:id)
|
||||
|
||||
|
@ -197,20 +256,6 @@ module Namespaces
|
|||
|
||||
Gitlab::SQL::CTE.new(:superset, superset_sql, materialized: false)
|
||||
end
|
||||
|
||||
def ancestor_ctes
|
||||
base_scope = all.select('namespaces.id', 'namespaces.traversal_ids')
|
||||
base_cte = Gitlab::SQL::CTE.new(:base_ancestors_cte, base_scope)
|
||||
|
||||
# We have to alias id with 'AS' to avoid ambiguous column references by calling methods.
|
||||
ancestors_scope = unscoped
|
||||
.unscope(where: [:type])
|
||||
.select('id as base_id', 'unnest(traversal_ids) as ancestor_id')
|
||||
.from(base_cte.table)
|
||||
ancestors_cte = Gitlab::SQL::CTE.new(:ancestors_cte, ancestors_scope)
|
||||
|
||||
[ancestors_cte, base_cte]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,50 @@ module WorkItems
|
|||
class ParentLink < ApplicationRecord
|
||||
self.table_name = 'work_item_parent_links'
|
||||
|
||||
MAX_CHILDREN = 100
|
||||
|
||||
belongs_to :work_item
|
||||
belongs_to :work_item_parent, class_name: 'WorkItem'
|
||||
|
||||
validates :work_item, :work_item_parent, presence: true
|
||||
validate :validate_child_type
|
||||
validate :validate_parent_type
|
||||
validate :validate_same_project
|
||||
validate :validate_max_children
|
||||
|
||||
private
|
||||
|
||||
def validate_child_type
|
||||
return unless work_item
|
||||
|
||||
unless work_item.task?
|
||||
errors.add :work_item, _('Only Task can be assigned as a child in hierarchy.')
|
||||
end
|
||||
end
|
||||
|
||||
def validate_parent_type
|
||||
return unless work_item_parent
|
||||
|
||||
unless work_item_parent.issue?
|
||||
errors.add :work_item_parent, _('Only Issue can be parent of Task.')
|
||||
end
|
||||
end
|
||||
|
||||
def validate_same_project
|
||||
return if work_item.nil? || work_item_parent.nil?
|
||||
|
||||
if work_item.resource_parent != work_item_parent.resource_parent
|
||||
errors.add :work_item_parent, _('Parent must be in the same project as child.')
|
||||
end
|
||||
end
|
||||
|
||||
def validate_max_children
|
||||
return unless work_item_parent
|
||||
|
||||
max = persisted? ? MAX_CHILDREN : MAX_CHILDREN - 1
|
||||
if work_item_parent.child_links.count > max
|
||||
errors.add :work_item_parent, _('Parent already has maximum number of children.')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,11 +13,10 @@ module Ci
|
|||
def execute
|
||||
return unless @user.present? && @user.can?(:update_runners_registration_token, scope)
|
||||
|
||||
case scope
|
||||
when ::ApplicationSetting
|
||||
if scope.respond_to?(:runners_registration_token)
|
||||
scope.reset_runners_registration_token!
|
||||
ApplicationSetting.current_without_cache.runners_registration_token
|
||||
when ::Group, ::Project
|
||||
scope.runners_registration_token
|
||||
else
|
||||
scope.reset_runners_token!
|
||||
scope.runners_token
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
= yield :flash_message
|
||||
= dispensable_render "shared/service_ping_consent"
|
||||
= dispensable_render_if_exists "layouts/header/ee_subscribable_banner"
|
||||
= dispensable_render_if_exists "layouts/header/seats_count_alert"
|
||||
= dispensable_render_if_exists "layouts/header/seat_count_alert"
|
||||
= dispensable_render_if_exists "shared/namespace_storage_limit_alert"
|
||||
= dispensable_render_if_exists "shared/namespace_user_cap_reached_alert"
|
||||
= dispensable_render_if_exists "shared/new_user_signups_cap_reached_alert"
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: group_level_protected_environment
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88506
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363450
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::release
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: seat_count_alerts
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79563
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/362041
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::purchase
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: use_traversal_ids_for_ancestor_scopes_with_inner_join
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83371
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/356628
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::workspace
|
||||
default_enabled: false
|
|
@ -54,7 +54,9 @@ required number of seconds.
|
|||
"required": [
|
||||
"id",
|
||||
"path",
|
||||
"created_at"
|
||||
"created_at",
|
||||
"shared_runners_enabled",
|
||||
"group_runners_enabled"
|
||||
],
|
||||
"properties": {
|
||||
"id": { "type": "integer" },
|
||||
|
|
|
@ -135,6 +135,9 @@ builds archival, to make it consistent and reliable.
|
|||
|
||||
Epic: [Partition CI/CD pipelines database tables](https://gitlab.com/groups/gitlab-org/-/epics/5417).
|
||||
|
||||
For more technical details about this topic see
|
||||
[pipeline data partitioning design](pipeline_partitioning.md).
|
||||
|
||||
### Partition CI/CD builds queuing database tables
|
||||
|
||||
While working on the [CI/CD Scale](../ci_scale/index.md) blueprint, we have
|
||||
|
@ -159,6 +162,9 @@ business rule is present in the product since the inception of GitLab CI.
|
|||
|
||||
Epic: [Partition CI/CD builds queuing database tables](https://gitlab.com/groups/gitlab-org/-/epics/7438).
|
||||
|
||||
For more technical details about this topic see
|
||||
[pipeline data partitioning design](pipeline_partitioning.md).
|
||||
|
||||
## Principles
|
||||
|
||||
All the three tracks we will use to implement CI/CD time decay pattern are
|
||||
|
|
|
@ -148,12 +148,12 @@ This sync job runs daily around 3AM UTC. If the job fails, it is retried up to 1
|
|||
|
||||
The daily job provides **only** the following information to the Customers Portal:
|
||||
|
||||
- Company name
|
||||
- Licensee name
|
||||
- Licensee email
|
||||
- Date
|
||||
- Timestamp
|
||||
- License key
|
||||
- Company name (encrypted within license key)
|
||||
- Licensee name (encrypted within license key)
|
||||
- Licensee email (encrypted within license key)
|
||||
- Historical maximum user count
|
||||
- Billable users count
|
||||
- GitLab version
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
---
|
||||
type: reference, howto
|
||||
stage: Manage
|
||||
group: Import
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Migrate groups from another instance of GitLab **(FREE)**
|
||||
# Migrating groups **(FREE)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/249160) in GitLab 13.7 [with a flag](../../feature_flags.md) named `bulk_import`. Disabled by default.
|
||||
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/338985) in GitLab 14.3.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../../../administration/feature_flags.md) named `bulk_import`. On GitLab.com, this feature is available.
|
||||
On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to
|
||||
[disable the feature flag](../../../administration/feature_flags.md) named `bulk_import`. On GitLab.com, this feature is
|
||||
available.
|
||||
|
||||
You can migrate your existing top-level groups to any of the following:
|
||||
|
||||
- Another GitLab instance, including GitLab.com.
|
||||
- Another top-level group.
|
||||
- The subgroup of any existing top-level group.
|
||||
- Another GitLab instance, including GitLab.com.
|
||||
|
||||
Migrating groups is not the same as [group import/export](../settings/import_export.md).
|
||||
|
||||
- Group import/export requires you to export a group to a file and then import that file in
|
||||
another GitLab instance.
|
||||
- Group migration automates this process.
|
||||
Migrating groups using the method documented here is not the same as [migrating groups using file exports](../settings/import_export.md).
|
||||
Importing and exporting groups using file exports requires you to export a group to a file and then import that file in
|
||||
another GitLab instance. Migrating groups using the method documented here automates this step.
|
||||
|
||||
## Import your groups into GitLab
|
||||
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
---
|
||||
type: reference
|
||||
stage: Manage
|
||||
group: Import
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Group import/export **(FREE)**
|
||||
# Migrating groups using file exports (deprecated) **(FREE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2888) in GitLab 13.0 as an experimental feature. May change in future releases.
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2888) in GitLab 13.0 as an experimental feature. May change in future releases.
|
||||
> - [Deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619) in GitLab 14.6.
|
||||
|
||||
You can export groups, with all their related data, from one GitLab instance to another.
|
||||
You can also [export projects](../../project/settings/import_export.md).
|
||||
WARNING:
|
||||
This feature was [deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619) in GitLab 14.6 and replaced by
|
||||
[a different migration method](../import/index.md). To follow progress on a solution for
|
||||
[offline environments](../../application_security/offline_deployments/index.md), see
|
||||
[the relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/363406).
|
||||
|
||||
You can export groups, with all their related data, from one GitLab instance to another. You can also:
|
||||
|
||||
- [Migrate groups](../import/index.md) using the preferred method.
|
||||
- [Migrate projects using file exports](../../project/settings/import_export.md).
|
||||
|
||||
## Enable export for a group
|
||||
|
||||
|
@ -63,10 +71,6 @@ For more details on the specific data persisted in a group export, see the
|
|||
|
||||
## Export a group
|
||||
|
||||
WARNING:
|
||||
This feature will be [deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619)
|
||||
in GitLab 14.6 and replaced by [GitLab Migration](../import/).
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have the Owner role for the group.
|
||||
|
@ -96,16 +100,11 @@ You can export groups from the [Community Edition to the Enterprise Edition](htt
|
|||
|
||||
The Enterprise Edition retains some group data that isn't part of the Community Edition. If you're exporting a group from the Enterprise Edition to the Community Edition, you may lose this data. For more information, see [downgrading from EE to CE](../../../index.md).
|
||||
|
||||
## Importing the group
|
||||
|
||||
WARNING:
|
||||
This feature will be [deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619)
|
||||
in GitLab 14.8 and replaced by [GitLab Migration](../import/).
|
||||
## Import the group
|
||||
|
||||
1. Create a new group:
|
||||
- On the top bar, select **New** (**{plus}**) and then **New group**.
|
||||
- On an existing group's page, select the **New subgroup** button.
|
||||
|
||||
1. Select **Import group**.
|
||||
1. Enter your group name.
|
||||
1. Accept or modify the associated group URL.
|
||||
|
|
|
@ -366,7 +366,7 @@ The chart shows data for the last 500 workflow items.
|
|||
- In the **From** field, select a start date.
|
||||
- In the **To** field, select an end date.
|
||||
|
||||
## Type of work - Tasks by type chart
|
||||
## Tasks by type chart
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32421) in GitLab 12.10.
|
||||
|
||||
|
|
|
@ -4,10 +4,13 @@ group: Import
|
|||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
|
||||
---
|
||||
|
||||
# Project import/export **(FREE)**
|
||||
# Migrating projects using file exports **(FREE)**
|
||||
|
||||
Existing projects on any self-managed GitLab instance or GitLab.com can be exported to a file and
|
||||
then imported into a new GitLab instance.
|
||||
then imported into a new GitLab instance. You can also:
|
||||
|
||||
- [Migrate groups](../../group/import/index.md) using the preferred method.
|
||||
- [Migrate groups using file exports](../../group/settings/import_export.md).
|
||||
|
||||
## Set up project import/export
|
||||
|
||||
|
@ -34,8 +37,8 @@ Before you can import a project, you must export it.
|
|||
|
||||
Prerequisites:
|
||||
|
||||
- Review the list of [data that will be exported](#items-that-are-exported).
|
||||
Not all data is exported.
|
||||
- Review the list of [items that are exported](#items-that-are-exported).
|
||||
Not all items are exported.
|
||||
- You must have at least the Maintainer role for the project.
|
||||
|
||||
To export a project and its data, follow these steps:
|
||||
|
|
|
@ -83,7 +83,9 @@ module Gitlab
|
|||
project: {
|
||||
id: project.id,
|
||||
path: project.full_path,
|
||||
created_at: project.created_at&.iso8601
|
||||
created_at: project.created_at&.iso8601,
|
||||
shared_runners_enabled: project.shared_runners_enabled?,
|
||||
group_runners_enabled: project.group_runners_enabled?
|
||||
},
|
||||
user: {
|
||||
id: current_user.id,
|
||||
|
|
|
@ -63,6 +63,8 @@ module Gitlab
|
|||
end
|
||||
|
||||
def build_mail
|
||||
# See https://github.com/mikel/mail/blob/641060598f8f4be14d79bad8d703e9f2967e1cdb/spec/mail/message_spec.rb#L569
|
||||
# for mail structure
|
||||
Mail::Message.new(@raw)
|
||||
rescue Encoding::UndefinedConversionError,
|
||||
Encoding::InvalidByteSequenceError => e
|
||||
|
|
|
@ -23,6 +23,12 @@ module Gitlab
|
|||
|
||||
config.address.sub(Gitlab::IncomingEmail::WILDCARD_PLACEHOLDER, key)
|
||||
end
|
||||
|
||||
def key_from_fallback_message_id(mail_id)
|
||||
message_id_regexp = /\Areply\-(.+)@#{Gitlab.config.gitlab.host}\z/
|
||||
|
||||
mail_id[message_id_regexp, 1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7173,6 +7173,9 @@ msgstr ""
|
|||
msgid "Cannot assign a confidential epic to a non-confidential issue. Make the issue confidential and try again"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cannot assign an issue that does not belong under the same group (or descendant) as the epic."
|
||||
msgstr ""
|
||||
|
||||
msgid "Cannot be merged automatically"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11193,17 +11196,6 @@ msgstr ""
|
|||
msgid "CycleAnalytics|Show"
|
||||
msgstr ""
|
||||
|
||||
msgid "CycleAnalytics|Showing %{subjectFilterText} and %{selectedLabelsCount} label"
|
||||
msgid_plural "CycleAnalytics|Showing %{subjectFilterText} and %{selectedLabelsCount} labels"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "CycleAnalytics|Showing data for group '%{groupName}' and %{selectedProjectCount} projects from %{createdAfter} to %{createdBefore}"
|
||||
msgstr ""
|
||||
|
||||
msgid "CycleAnalytics|Showing data for group '%{groupName}' from %{createdAfter} to %{createdBefore}"
|
||||
msgstr ""
|
||||
|
||||
msgid "CycleAnalytics|Stage time: %{title}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11231,9 +11223,6 @@ msgstr ""
|
|||
msgid "CycleAnalytics|Total time"
|
||||
msgstr ""
|
||||
|
||||
msgid "CycleAnalytics|Type of work"
|
||||
msgstr ""
|
||||
|
||||
msgid "CycleAnalytics|group dropdown filter"
|
||||
msgstr ""
|
||||
|
||||
|
@ -15019,6 +15008,9 @@ msgstr ""
|
|||
msgid "Estimated"
|
||||
msgstr ""
|
||||
|
||||
msgid "Even if you reach the number of seats in your subscription, you can continue to add users, and GitLab will bill you for the overage."
|
||||
msgstr ""
|
||||
|
||||
msgid "EventFilterBy|Filter by all"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26564,9 +26556,15 @@ msgstr ""
|
|||
msgid "Only Issue ID or merge request ID is required"
|
||||
msgstr ""
|
||||
|
||||
msgid "Only Issue can be parent of Task."
|
||||
msgstr ""
|
||||
|
||||
msgid "Only Project Members"
|
||||
msgstr ""
|
||||
|
||||
msgid "Only Task can be assigned as a child in hierarchy."
|
||||
msgstr ""
|
||||
|
||||
msgid "Only active projects show up in the search and on the dashboard."
|
||||
msgstr ""
|
||||
|
||||
|
@ -27319,12 +27317,18 @@ msgstr ""
|
|||
msgid "Parent"
|
||||
msgstr ""
|
||||
|
||||
msgid "Parent already has maximum number of children."
|
||||
msgstr ""
|
||||
|
||||
msgid "Parent epic doesn't exist."
|
||||
msgstr ""
|
||||
|
||||
msgid "Parent epic is not present."
|
||||
msgstr ""
|
||||
|
||||
msgid "Parent must be in the same project as child."
|
||||
msgstr ""
|
||||
|
||||
msgid "Parsing error for param :embed_json. %{message}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -41628,6 +41632,11 @@ msgstr ""
|
|||
msgid "ValueStreamAnalytics|%{stageCount}+ items"
|
||||
msgstr ""
|
||||
|
||||
msgid "ValueStreamAnalytics|%{subjectFilterText} and %{selectedLabelsCount} label"
|
||||
msgid_plural "ValueStreamAnalytics|%{subjectFilterText} and %{selectedLabelsCount} labels"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "ValueStreamAnalytics|%{value}M"
|
||||
msgstr ""
|
||||
|
||||
|
@ -41682,13 +41691,22 @@ msgstr ""
|
|||
msgid "ValueStreamAnalytics|Percentage of deployments that cause an incident in production."
|
||||
msgstr ""
|
||||
|
||||
msgid "ValueStreamAnalytics|Shows %{selectedFiltersDescription} for group '%{groupName}' and %{selectedProjectCount} projects from %{createdAfter} to %{createdBefore}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ValueStreamAnalytics|Shows %{selectedFiltersDescription} for group '%{groupName}' from %{createdAfter} to %{createdBefore}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ValueStreamAnalytics|Tasks by type"
|
||||
msgstr ""
|
||||
|
||||
msgid "ValueStreamAnalytics|There was an error while fetching value stream analytics %{requestTypeName} data."
|
||||
msgstr ""
|
||||
|
||||
msgid "ValueStreamAnalytics|Total number of deploys to production."
|
||||
msgstr ""
|
||||
|
||||
msgid "ValueStreamAnalytics|Value Stream"
|
||||
msgid "ValueStreamAnalytics|Value stream"
|
||||
msgstr ""
|
||||
|
||||
msgid "ValueStreamEvent|Items in stage"
|
||||
|
@ -44175,8 +44193,10 @@ msgstr ""
|
|||
msgid "Your subscription expired!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your subscription has %{remaining_seats_count} out of %{total_seats_count} seats remaining. Even if you reach the number of seats in your subscription, you can continue to add users, and GitLab will bill you for the overage."
|
||||
msgstr ""
|
||||
msgid "Your subscription has %{remaining_seat_count} out of %{total_seat_count} seat remaining."
|
||||
msgid_plural "Your subscription has %{remaining_seat_count} out of %{total_seat_count} seats remaining."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "Your subscription is now expired. To renew, export your license usage file and email it to %{renewal_service_email}. A new license will be emailed to the email address registered in the %{customers_dot}. You can add this license to your instance. To use Free tier, remove your current license."
|
||||
msgstr ""
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
"codesandbox-api": "0.0.23",
|
||||
"compression-webpack-plugin": "^5.0.2",
|
||||
"copy-webpack-plugin": "^6.4.1",
|
||||
"core-js": "^3.22.7",
|
||||
"core-js": "^3.22.8",
|
||||
"cron-validator": "^1.1.1",
|
||||
"cronstrue": "^1.122.0",
|
||||
"cropper": "^2.3.0",
|
||||
|
|
|
@ -31,6 +31,7 @@ RSpec.describe Registrations::WelcomeController do
|
|||
|
||||
context 'when role and setup_for_company is set' do
|
||||
before do
|
||||
stub_feature_flags(about_your_company_registration_flow: false)
|
||||
user.update!(setup_for_company: false)
|
||||
sign_in(user)
|
||||
end
|
||||
|
@ -60,6 +61,10 @@ RSpec.describe Registrations::WelcomeController do
|
|||
end
|
||||
|
||||
describe '#update' do
|
||||
before do
|
||||
stub_feature_flags(about_your_company_registration_flow: false)
|
||||
end
|
||||
|
||||
subject(:update) do
|
||||
patch :update, params: { user: { role: 'software_developer', setup_for_company: 'false' } }
|
||||
end
|
||||
|
|
|
@ -9,5 +9,10 @@ FactoryBot.define do
|
|||
relative_position { RelativePositioning::START_POSITION }
|
||||
issue_type { :issue }
|
||||
association :work_item_type, :default
|
||||
|
||||
trait :task do
|
||||
issue_type { :task }
|
||||
association :work_item_type, :default, :task
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :parent_link, class: 'WorkItems::ParentLink' do
|
||||
transient do
|
||||
work_item { nil }
|
||||
work_item_parent { nil }
|
||||
end
|
||||
|
||||
after(:build) do |link, evaluator|
|
||||
link.work_item = evaluator.work_item
|
||||
link.work_item_parent = evaluator.work_item_parent
|
||||
|
||||
unless link.work_item && link.work_item_parent
|
||||
project = link.work_item&.project || link.work_item_parent&.project || create(:project)
|
||||
link.work_item ||= create(:work_item, :task, project: project)
|
||||
link.work_item_parent ||= create(:work_item, project: project)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -341,6 +341,7 @@ RSpec.describe 'Signup' do
|
|||
end
|
||||
|
||||
it 'redirects to step 2 of the signup process, sets the role and redirects back' do
|
||||
stub_feature_flags(about_your_company_registration_flow: false)
|
||||
visit new_user_registration_path
|
||||
|
||||
fill_in_signup_form
|
||||
|
|
|
@ -12,12 +12,16 @@
|
|||
"required": [
|
||||
"id",
|
||||
"path",
|
||||
"created_at"
|
||||
"created_at",
|
||||
"shared_runners_enabled",
|
||||
"group_runners_enabled"
|
||||
],
|
||||
"properties": {
|
||||
"id": { "type": "integer" },
|
||||
"path": { "type": "string" },
|
||||
"created_at": { "type": ["string", "null"], "format": "date-time" }
|
||||
"created_at": { "type": ["string", "null"], "format": "date-time" },
|
||||
"shared_runners_enabled": { "type": "boolean" },
|
||||
"group_runners_enabled": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
Return-Path: <jake@example.com>
|
||||
Received: from myserver.example.com ([unix socket]) by myserver (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
|
||||
Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
|
||||
Received: from blabla.google.com (blabla.google.com. [1.1.1.1])
|
||||
by bla.google.com with SMTPS id something.1.1.1.1.1.1.1
|
||||
for <abc@appmail.example.com>
|
||||
(Google Transport Security);
|
||||
Mon, 21 Feb 2022 14:41:58 -0800 (PST)
|
||||
References: <topic/35@some.other.server.example.com>
|
||||
<abc123@discourse-app.mail>
|
||||
Received: from mail.example.com (mail.example.com [IPv6:2607:f8b0:4001:c03::234]) by myserver.example.com (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@example.com>; Thu, 13 Jun 2013 17:03:50 -0400
|
||||
From: "jake@example.com" <jake@example.com>
|
||||
To: "some_unrelated_email@example.com" <some_unrelated_email@example.com>
|
||||
Subject: Re: Insert hilarious subject line here
|
||||
Date: Tue, 26 Nov 2019 14:22:41 +0000
|
||||
Message-ID: <7e2296f83dbf4de388cbf5f56f52c11f@EXDAG29-1.EXCHANGE.INT>
|
||||
Content-Type: multipart/alternative;
|
||||
boundary="_000_7e2296f83dbf4de388cbf5f56f52c11fEXDAG291EXCHANGEINT_"
|
||||
MIME-Version: 1.0
|
||||
|
||||
--_000_7e2296f83dbf4de388cbf5f56f52c11fEXDAG291EXCHANGEINT_
|
||||
Content-Type: text/plain; charset="iso-8859-1"
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
|
||||
|
||||
--_000_7e2296f83dbf4de388cbf5f56f52c11fEXDAG291EXCHANGEINT_
|
||||
Content-Type: text/html; charset="iso-8859-1"
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
This message has unhelpful References headers with no key, and no key in the To headers either.
|
|
@ -778,3 +778,15 @@ export const boardListQueryResponse = (issuesCount = 20) => ({
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const epicBoardListQueryResponse = (totalWeight = 5) => ({
|
||||
data: {
|
||||
epicBoardList: {
|
||||
__typename: 'EpicList',
|
||||
id: 'gid://gitlab/Boards::EpicList/3',
|
||||
metadata: {
|
||||
totalWeight,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -53,6 +53,18 @@ RSpec.describe Gitlab::Email::ServiceDeskReceiver do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the email contains no key in the To header and contains reference header with no key' do
|
||||
let(:email) { fixture_file('emails/service_desk_reference_headers.eml') }
|
||||
|
||||
before do
|
||||
stub_service_desk_email_setting(enabled: true, address: 'support+%{key}@example.com')
|
||||
end
|
||||
|
||||
it 'sends a rejection email' do
|
||||
expect { receiver.execute }.to raise_error(Gitlab::Email::UnknownIncomingEmail)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the email does not contain a valid email address' do
|
||||
before do
|
||||
stub_service_desk_email_setting(enabled: true, address: 'other_support+%{key}@example.com')
|
||||
|
|
|
@ -78,4 +78,10 @@ RSpec.describe Gitlab::ServiceDeskEmail do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'self.key_from_fallback_message_id' do
|
||||
it 'returns reply key' do
|
||||
expect(described_class.key_from_fallback_message_id('reply-key@localhost')).to eq('key')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe AsCte do
|
||||
let(:klass) do
|
||||
Class.new(ApplicationRecord) do
|
||||
include AsCte
|
||||
|
||||
self.table_name = 'users'
|
||||
end
|
||||
end
|
||||
|
||||
let(:query) { klass.where(id: [1, 2, 3]) }
|
||||
let(:name) { :klass_cte }
|
||||
|
||||
describe '.as_cte' do
|
||||
subject { query.as_cte(name) }
|
||||
|
||||
it { expect(subject).to be_a(Gitlab::SQL::CTE) }
|
||||
it { expect(subject.query).to eq(query) }
|
||||
it { expect(subject.table.name).to eq(name.to_s) }
|
||||
|
||||
context 'with materialized parameter' do
|
||||
subject { query.as_cte(name, materialized: materialized).to_arel.to_sql }
|
||||
|
||||
context 'as true' do
|
||||
let(:materialized) { true }
|
||||
|
||||
it { expect(subject).to match /MATERIALIZE/ }
|
||||
end
|
||||
|
||||
context 'as false' do
|
||||
let(:materialized) { false }
|
||||
|
||||
it { expect(subject).not_to match /MATERIALIZE/ }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -375,6 +375,14 @@ RSpec.describe Namespace do
|
|||
|
||||
context 'linear' do
|
||||
it_behaves_like 'namespace traversal scopes'
|
||||
|
||||
context 'without inner join ancestors query' do
|
||||
before do
|
||||
stub_feature_flags(use_traversal_ids_for_ancestor_scopes_with_inner_join: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'namespace traversal scopes'
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'makes recursive queries' do
|
||||
|
|
|
@ -7,4 +7,60 @@ RSpec.describe WorkItems::ParentLink do
|
|||
it { is_expected.to belong_to(:work_item) }
|
||||
it { is_expected.to belong_to(:work_item_parent).class_name('WorkItem') }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
it { is_expected.to validate_presence_of(:work_item) }
|
||||
it { is_expected.to validate_presence_of(:work_item_parent) }
|
||||
|
||||
describe 'hierarchy' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:issue) { build(:work_item, project: project) }
|
||||
let_it_be(:task1) { build(:work_item, :task, project: project) }
|
||||
let_it_be(:task2) { build(:work_item, :task, project: project) }
|
||||
|
||||
it 'is valid if not-task parent has task child' do
|
||||
expect(build(:parent_link, work_item: task1, work_item_parent: issue)).to be_valid
|
||||
end
|
||||
|
||||
it 'is not valid if child is not task' do
|
||||
link = build(:parent_link, work_item: issue)
|
||||
|
||||
expect(link).not_to be_valid
|
||||
expect(link.errors[:work_item]).to include('Only Task can be assigned as a child in hierarchy.')
|
||||
end
|
||||
|
||||
it 'is not valid if parent is task' do
|
||||
link = build(:parent_link, work_item_parent: task1)
|
||||
|
||||
expect(link).not_to be_valid
|
||||
expect(link.errors[:work_item_parent]).to include('Only Issue can be parent of Task.')
|
||||
end
|
||||
|
||||
it 'is not valid if parent is in other project' do
|
||||
link = build(:parent_link, work_item_parent: task1, work_item: build(:work_item))
|
||||
|
||||
expect(link).not_to be_valid
|
||||
expect(link.errors[:work_item_parent]).to include('Parent must be in the same project as child.')
|
||||
end
|
||||
|
||||
context 'when parent already has maximum number of links' do
|
||||
let_it_be(:link1) { create(:parent_link, work_item_parent: issue, work_item: task1) }
|
||||
|
||||
before do
|
||||
stub_const("#{described_class}::MAX_CHILDREN", 1)
|
||||
end
|
||||
|
||||
it 'is not valid when another link is added' do
|
||||
link2 = build(:parent_link, work_item_parent: issue, work_item: task2)
|
||||
|
||||
expect(link2).not_to be_valid
|
||||
expect(link2.errors[:work_item_parent]).to include('Parent already has maximum number of children.')
|
||||
end
|
||||
|
||||
it 'existing link is still valid' do
|
||||
expect(link1).to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3847,10 +3847,10 @@ core-js-pure@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
||||
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
|
||||
|
||||
core-js@^3.22.7:
|
||||
version "3.22.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.22.7.tgz#8d6c37f630f6139b8732d10f2c114c3f1d00024f"
|
||||
integrity sha512-Jt8SReuDKVNZnZEzyEQT5eK6T2RRCXkfTq7Lo09kpm+fHjgGewSbNjV+Wt4yZMhPDdzz2x1ulI5z/w4nxpBseg==
|
||||
core-js@^3.22.8:
|
||||
version "3.22.8"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.22.8.tgz#23f860b1fe60797cc4f704d76c93fea8a2f60631"
|
||||
integrity sha512-UoGQ/cfzGYIuiq6Z7vWL1HfkE9U9IZ4Ub+0XSiJTCzvbZzgPA69oDF2f+lgJ6dFFLEdjW5O6svvoKzXX23xFkA==
|
||||
|
||||
core-js@~2.3.0:
|
||||
version "2.3.0"
|
||||
|
|
Loading…
Reference in New Issue