Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-06-16 18:09:42 +00:00
parent 0c87da9375
commit c44eade0d7
64 changed files with 1287 additions and 210 deletions

View File

@ -39,7 +39,7 @@ dont-interrupt-me:
<% if test_suite_prefix.nil? %>
.base-rspec-predictive:
extends:
- .rspec-base-pg12
- .rspec-base-pg13
- .base-predictive
variables:
# We're using the FOSS one here because we want to exclude EE-only ones
@ -98,7 +98,7 @@ rspec system predictive:
<% if test_suite_prefix == 'ee/' %>
.base-rspec-ee-predictive:
extends:
- .rspec-ee-base-pg12
- .rspec-ee-base-pg13
- .base-predictive
variables:
RSPEC_TESTS_FILTER_FILE: "${RSPEC_MATCHING_TESTS_EE_PATH}"

View File

@ -152,7 +152,7 @@ export default {
<span class="note-headline-light note-headline-meta">
<span class="system-note-message"> <slot></slot> </span>
<gl-link
class="note-timestamp system-note-separator gl-display-block gl-mb-2"
class="note-timestamp system-note-separator gl-display-block gl-mb-2 gl-font-sm"
:href="`#note_${noteAnchorId}`"
>
<time-ago-tooltip :time="note.createdAt" tooltip-placement="bottom" />

View File

@ -39,31 +39,31 @@ export default {
<template>
<li
class="toggle-comments gl-bg-gray-50 gl-display-flex gl-align-items-center gl-py-3"
class="toggle-comments gl-bg-gray-50 gl-display-flex gl-align-items-center gl-py-3 gl-min-h-8"
:class="{ expanded: !collapsed }"
data-testid="toggle-comments-wrapper"
>
<gl-icon :name="iconName" class="gl-ml-3" @click.stop="$emit('toggle')" />
<gl-button
variant="link"
class="toggle-comments-button gl-ml-2 gl-mr-2"
class="toggle-comments-button gl-ml-2 gl-mr-2 gl-font-sm!"
@click.stop="$emit('toggle')"
>
{{ toggleText }}
</gl-button>
<template v-if="collapsed">
<span class="gl-text-gray-500">{{ __('Last reply by') }}</span>
<span class="gl-text-gray-500 gl-font-sm">{{ __('Last reply by') }}</span>
<gl-link
:href="lastReply.author.webUrl"
target="_blank"
class="link-inherit-color gl-text-body gl-text-decoration-none gl-font-weight-bold gl-ml-2 gl-mr-2"
class="link-inherit-color gl-text-body gl-text-decoration-none gl-font-weight-bold gl-font-sm gl-ml-2 gl-mr-2"
>
{{ lastReply.author.name }}
</gl-link>
<time-ago-tooltip
:time="lastReply.createdAt"
tooltip-placement="bottom"
class="gl-text-gray-500"
class="gl-text-gray-500 gl-font-sm"
/>
</template>
</li>

View File

@ -22,7 +22,7 @@ export const containsSensitiveToken = (message) => {
{
// eslint-disable-next-line @gitlab/require-i18n-strings
name: 'Feed Token',
regex: 'feed_token=[0-9a-zA-Z_-]{20}',
regex: 'feed_token=((glft-)?[0-9a-zA-Z_-]{20}|glft-[a-h0-9]+-[0-9]+_)',
},
];

View File

@ -11,7 +11,7 @@ const PERSISTENT_USER_CALLOUTS = [
'.js-eoa-bronze-plan-banner',
'.js-security-newsletter-callout',
'.js-approaching-seat-count-threshold',
'.js-storage-enforcement-banner',
'.js-storage-pre-enforcement-alert',
'.js-user-over-limit-free-plan-alert',
'.js-minute-limit-banner',
'.js-submit-license-usage-data-banner',

View File

@ -0,0 +1,24 @@
import iconUrl from 'leaflet/dist/images/marker-icon.png';
import iconRetinaUrl from 'leaflet/dist/images/marker-icon-2x.png';
import shadowUrl from 'leaflet/dist/images/marker-shadow.png';
import { __ } from '~/locale';
export const RENDER_ERROR_MSG = __(
'The map can not be displayed because there was an error loading the GeoJSON file.',
);
export const OPEN_STREET_TILE_URL = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
export const ICON_CONFIG = { iconUrl, iconRetinaUrl, shadowUrl };
export const MAP_ATTRIBUTION = __('Map data from');
export const OPEN_STREET_COPYRIGHT_LINK =
'<a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">OpenStreetMap</a>';
export const POPUP_CONTENT_TEMPLATE = `
<div class="gl-pt-4">
<% eachFunction(popupProperties, function(value, label) { %>
<div>
<strong><%- label %>:</strong> <span><%- value %></span>
</div>
<% }); %>
</div>
`;

View File

@ -0,0 +1,32 @@
<script>
import { createAlert } from '~/alert';
import { RENDER_ERROR_MSG } from './constants';
import { initLeafletMap } from './utils';
export default {
props: {
blob: {
type: Object,
required: true,
},
},
data() {
return {
hasError: false,
loading: true,
};
},
mounted() {
try {
initLeafletMap(this.$refs.map, JSON.parse(this.blob.rawTextBlob));
} catch (error) {
createAlert({ message: RENDER_ERROR_MSG });
this.hasError = true;
}
},
};
</script>
<template>
<div v-if="!hasError" ref="map" class="gl-h-100vh gl-z-index-0" data-testid="map"></div>
</template>

View File

@ -0,0 +1,47 @@
import { map, tileLayer, geoJson, featureGroup, Icon } from 'leaflet';
import { template, each } from 'lodash';
import {
OPEN_STREET_TILE_URL,
MAP_ATTRIBUTION,
OPEN_STREET_COPYRIGHT_LINK,
ICON_CONFIG,
POPUP_CONTENT_TEMPLATE,
} from './constants';
const generateOpenStreetMapTiles = () => {
const attribution = `${MAP_ATTRIBUTION} ${OPEN_STREET_COPYRIGHT_LINK}`;
return tileLayer(OPEN_STREET_TILE_URL, { attribution });
};
export const popupContent = (popupProperties) => {
return template(POPUP_CONTENT_TEMPLATE)({
eachFunction: each,
popupProperties,
});
};
const loadGeoJsonGroupAndBounds = (geoJsonData) => {
const layers = [];
const geoJsonGroup = geoJson(geoJsonData, {
onEachFeature: (feature, layer) => {
layers.push(layer);
if (feature.properties) {
layer.bindPopup(popupContent(feature.properties));
}
},
});
return { geoJsonGroup, bounds: featureGroup(layers).getBounds() };
};
export const initLeafletMap = (el, geoJsonData) => {
if (!el || !geoJsonData) return;
import('leaflet/dist/leaflet.css');
Icon.Default.mergeOptions(ICON_CONFIG);
const leafletMap = map(el, { layers: [generateOpenStreetMapTiles()] });
const { bounds, geoJsonGroup } = loadGeoJsonGroupAndBounds(geoJsonData);
geoJsonGroup.addTo(leafletMap);
leafletMap.fitBounds(bounds);
};

View File

@ -12,6 +12,7 @@ const viewers = {
sketch: () => import('./sketch_viewer.vue'),
notebook: () => import('./notebook_viewer.vue'),
openapi: () => import('./openapi_viewer.vue'),
geo_json: () => import('./geo_json/geo_json_viewer.vue'),
};
export const loadViewer = (type, isUsingLfs) => {

View File

@ -138,7 +138,7 @@ $t-gray-a-16-design-pin: rgba($black, 0.16);
}
.design-note-pin {
margin-left: $gl-padding;
margin-left: 9px;
}
.design-discussion {
@ -148,13 +148,13 @@ $t-gray-a-16-design-pin: rgba($black, 0.16);
content: '';
border-left: 1px solid var(--gray-100, $gray-100);
position: absolute;
left: 28px;
left: 22px;
top: -17px;
height: 17px;
}
.design-note {
padding: $gl-padding;
padding: $gl-padding-8;
list-style: none;
transition: background $gl-transition-duration-medium $general-hover-transition-curve;
border-top-left-radius: $border-radius-default; // same border radius used by .bordered-box
@ -170,7 +170,9 @@ $t-gray-a-16-design-pin: rgba($black, 0.16);
}
.reply-wrapper {
padding: $gl-padding;
padding: $gl-padding-8 $gl-padding-8 $gl-padding-4;
background: $gray-10;
border-radius: 0 0 $border-radius-default $border-radius-default;
}
}

View File

@ -33,6 +33,7 @@ class Blob < SimpleDelegator
BlobViewer::Notebook,
BlobViewer::SVG,
BlobViewer::OpenApi,
BlobViewer::GeoJson,
BlobViewer::Image,
BlobViewer::Sketch,

View File

@ -0,0 +1,12 @@
# frozen_string_literal: true
module BlobViewer
class GeoJson < Base
include Rich
include ClientSide
self.binary = false
self.extensions = %w[geojson]
self.partial_name = 'geo_json'
end
end

View File

@ -38,7 +38,7 @@ class Group < Namespace
has_many :users, through: :group_members
has_many :owners,
-> { where(members: { access_level: Gitlab::Access::OWNER }) },
through: :group_members,
through: :all_group_members,
source: :user
has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent

View File

@ -57,10 +57,14 @@ class User < ApplicationRecord
FORBIDDEN_SEARCH_STATES = %w(blocked banned ldap_blocked).freeze
INCOMING_MAIL_TOKEN_PREFIX = 'glimt-'
FEED_TOKEN_PREFIX = 'glft-'
columns_changing_default :notified_of_own_activity
add_authentication_token_field :incoming_email_token, token_generator: -> { SecureRandom.hex.to_i(16).to_s(36) }
add_authentication_token_field :feed_token
# lib/tasks/tokens.rake needs to be updated when changing mail and feed tokens
add_authentication_token_field :incoming_email_token, token_generator: -> { self.generate_incoming_mail_token }
add_authentication_token_field :feed_token, format_with_prefix: :prefix_for_feed_token
add_authentication_token_field :static_object_token, encrypted: :optional
attribute :admin, default: false
@ -968,6 +972,10 @@ class User < ApplicationRecord
def get_ids_by_ids_or_usernames(ids, usernames)
by_ids_or_usernames(ids, usernames).pluck(:id)
end
def generate_incoming_mail_token
"#{INCOMING_MAIL_TOKEN_PREFIX}#{SecureRandom.hex.to_i(16).to_s(36)}"
end
end
#
@ -2596,6 +2604,10 @@ class User < ApplicationRecord
Ci::NamespaceMirror.contains_traversal_ids(traversal_ids)
end
def prefix_for_feed_token
FEED_TOKEN_PREFIX
end
end
User.prepend_mod_with('User')

View File

@ -2,7 +2,8 @@
- project = @project.present(current_user: current_user)
- ref = local_assigns[:ref] || @ref
- expanded = params[:expanded].present?
- if blob.rich_viewer
-# If the blob has a RichViewer we preload the content except for GeoJSON since it is handled by Vue
- if blob.rich_viewer && blob.extension != 'geojson'
- add_page_startup_api_call local_assigns.fetch(:viewer_url) { url_for(safe_params.merge(viewer: blob.rich_viewer.type, format: :json)) }
.info-well.d-none.d-sm-block

View File

@ -0,0 +1,8 @@
---
name: merge_commit_diff_modes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123501
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/415579
milestone: '16.1'
type: development
group: group::source code
default_enabled: false

View File

@ -40,7 +40,9 @@ else
# https://gitlab.com/gitlab-org/gitlab/-/issues/369970
/Passing an Active Record object to `\w+` directly is deprecated/,
# https://gitlab.com/gitlab-org/gitlab/-/issues/410086
/Using `return`, `break` or `throw` to exit a transaction block/
/Using `return`, `break` or `throw` to exit a transaction block/,
# https://gitlab.com/gitlab-org/gitlab/-/issues/414556
/Merging .* no longer maintain both conditions, and will be replaced by the latter in Rails 7\.0/
]
view_component_3_warnings = [

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class ReplaceOldFkCiBuildPendingStatesToBuilds < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
def up
return unless should_run?
return if new_foreign_key_exists?
with_lock_retries do
remove_foreign_key_if_exists :ci_build_pending_states, :ci_builds,
name: :fk_861cd17da3_p, reverse_lock_order: true
rename_constraint :ci_build_pending_states, :temp_fk_861cd17da3_p, :fk_861cd17da3_p
end
end
def down
return unless should_run?
return unless new_foreign_key_exists?
add_concurrent_foreign_key :ci_build_pending_states, :ci_builds,
name: :temp_fk_861cd17da3_p,
column: [:partition_id, :build_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :cascade,
validate: true,
reverse_lock_order: true
switch_constraint_names :ci_build_pending_states, :fk_861cd17da3_p, :temp_fk_861cd17da3_p
end
private
def should_run?
can_execute_on?(:ci_build_pending_states, :ci_builds)
end
def new_foreign_key_exists?
foreign_key_exists?(:ci_build_pending_states, :p_ci_builds, name: :fk_861cd17da3_p)
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class ReplaceOldFkCiBuildTraceChunksToBuilds < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
def up
return unless should_run?
return if new_foreign_key_exists?
with_lock_retries do
remove_foreign_key_if_exists :ci_build_trace_chunks, :ci_builds,
name: :fk_89e29fa5ee_p, reverse_lock_order: true
rename_constraint :ci_build_trace_chunks, :temp_fk_89e29fa5ee_p, :fk_89e29fa5ee_p
end
end
def down
return unless should_run?
return unless new_foreign_key_exists?
add_concurrent_foreign_key :ci_build_trace_chunks, :ci_builds,
name: :temp_fk_89e29fa5ee_p,
column: [:partition_id, :build_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :cascade,
validate: true,
reverse_lock_order: true
switch_constraint_names :ci_build_trace_chunks, :fk_89e29fa5ee_p, :temp_fk_89e29fa5ee_p
end
private
def should_run?
can_execute_on?(:ci_build_trace_chunks, :ci_builds)
end
def new_foreign_key_exists?
foreign_key_exists?(:ci_build_trace_chunks, :p_ci_builds, name: :fk_89e29fa5ee_p)
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class ReplaceOldFkCiUnitTestFailuresToBuilds < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
def up
return unless should_run?
return if new_foreign_key_exists?
with_lock_retries do
remove_foreign_key_if_exists :ci_unit_test_failures, :ci_builds,
name: :fk_9e0fc58930_p, reverse_lock_order: true
rename_constraint :ci_unit_test_failures, :temp_fk_9e0fc58930_p, :fk_9e0fc58930_p
end
end
def down
return unless should_run?
return unless new_foreign_key_exists?
add_concurrent_foreign_key :ci_unit_test_failures, :ci_builds,
name: :temp_fk_9e0fc58930_p,
column: [:partition_id, :build_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :cascade,
validate: true,
reverse_lock_order: true
switch_constraint_names :ci_unit_test_failures, :fk_9e0fc58930_p, :temp_fk_9e0fc58930_p
end
private
def should_run?
can_execute_on?(:ci_unit_test_failures, :ci_builds)
end
def new_foreign_key_exists?
foreign_key_exists?(:ci_unit_test_failures, :p_ci_builds, name: :fk_9e0fc58930_p)
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class ReplaceOldFkCiSourcesPipelinesToBuilds < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
def up
return unless should_run?
return if new_foreign_key_exists?
with_lock_retries do
remove_foreign_key_if_exists :ci_sources_pipelines, :ci_builds,
name: :fk_be5624bf37_p, reverse_lock_order: true
rename_constraint :ci_sources_pipelines, :temp_fk_be5624bf37_p, :fk_be5624bf37_p
end
end
def down
return unless should_run?
return unless new_foreign_key_exists?
add_concurrent_foreign_key :ci_sources_pipelines, :ci_builds,
name: :temp_fk_be5624bf37_p,
column: [:source_partition_id, :source_job_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :cascade,
validate: true,
reverse_lock_order: true
switch_constraint_names :ci_sources_pipelines, :fk_be5624bf37_p, :temp_fk_be5624bf37_p
end
private
def should_run?
can_execute_on?(:ci_sources_pipelines, :ci_builds)
end
def new_foreign_key_exists?
foreign_key_exists?(:ci_sources_pipelines, :p_ci_builds, name: :fk_be5624bf37_p)
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class ReplaceOldFkCiResourcesToBuilds < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
def up
return unless should_run?
return if new_foreign_key_exists?
with_lock_retries do
remove_foreign_key_if_exists :ci_resources, :ci_builds,
name: :fk_e169a8e3d5_p, reverse_lock_order: true
rename_constraint :ci_resources, :temp_fk_e169a8e3d5_p, :fk_e169a8e3d5_p
end
end
def down
return unless should_run?
return unless new_foreign_key_exists?
add_concurrent_foreign_key :ci_resources, :ci_builds,
name: :temp_fk_e169a8e3d5_p,
column: [:partition_id, :build_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :nullify,
validate: true,
reverse_lock_order: true
switch_constraint_names :ci_resources, :fk_e169a8e3d5_p, :temp_fk_e169a8e3d5_p
end
private
def should_run?
can_execute_on?(:ci_resources, :ci_builds)
end
def new_foreign_key_exists?
foreign_key_exists?(:ci_resources, :p_ci_builds, name: :fk_e169a8e3d5_p)
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class ReplaceOldFkCiBuildReportResultsToBuilds < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
def up
return unless should_run?
return if new_foreign_key_exists?
with_lock_retries do
remove_foreign_key_if_exists :ci_build_report_results, :ci_builds,
name: :fk_rails_16cb1ff064_p, reverse_lock_order: true
rename_constraint :ci_build_report_results, :temp_fk_rails_16cb1ff064_p, :fk_rails_16cb1ff064_p
end
end
def down
return unless should_run?
return unless new_foreign_key_exists?
add_concurrent_foreign_key :ci_build_report_results, :ci_builds,
name: :temp_fk_rails_16cb1ff064_p,
column: [:partition_id, :build_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :cascade,
validate: true,
reverse_lock_order: true
switch_constraint_names :ci_build_report_results, :fk_rails_16cb1ff064_p, :temp_fk_rails_16cb1ff064_p
end
private
def should_run?
can_execute_on?(:ci_build_report_results, :ci_builds)
end
def new_foreign_key_exists?
foreign_key_exists?(:ci_build_report_results, :p_ci_builds, name: :fk_rails_16cb1ff064_p)
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class ReplaceOldFkCiBuildNeedsToBuilds < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
def up
return unless should_run?
return if new_foreign_key_exists?
with_lock_retries do
remove_foreign_key_if_exists :ci_build_needs, :ci_builds,
name: :fk_rails_3cf221d4ed_p, reverse_lock_order: true
rename_constraint :ci_build_needs, :temp_fk_rails_3cf221d4ed_p, :fk_rails_3cf221d4ed_p
end
end
def down
return unless should_run?
return unless new_foreign_key_exists?
add_concurrent_foreign_key :ci_build_needs, :ci_builds,
name: :temp_fk_rails_3cf221d4ed_p,
column: [:partition_id, :build_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :cascade,
validate: true,
reverse_lock_order: true
switch_constraint_names :ci_build_needs, :fk_rails_3cf221d4ed_p, :temp_fk_rails_3cf221d4ed_p
end
private
def should_run?
can_execute_on?(:ci_build_needs, :ci_builds)
end
def new_foreign_key_exists?
foreign_key_exists?(:ci_build_needs, :p_ci_builds, name: :fk_rails_3cf221d4ed_p)
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class ReplaceOldFkCiBuildsRunnerSessionToBuilds < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
def up
return unless should_run?
return if new_foreign_key_exists?
with_lock_retries do
remove_foreign_key_if_exists :ci_builds_runner_session, :ci_builds,
name: :fk_rails_70707857d3_p, reverse_lock_order: true
rename_constraint :ci_builds_runner_session, :temp_fk_rails_70707857d3_p, :fk_rails_70707857d3_p
end
end
def down
return unless should_run?
return unless new_foreign_key_exists?
add_concurrent_foreign_key :ci_builds_runner_session, :ci_builds,
name: :temp_fk_rails_70707857d3_p,
column: [:partition_id, :build_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :cascade,
validate: true,
reverse_lock_order: true
switch_constraint_names :ci_builds_runner_session, :fk_rails_70707857d3_p, :temp_fk_rails_70707857d3_p
end
private
def should_run?
can_execute_on?(:ci_builds_runner_session, :ci_builds)
end
def new_foreign_key_exists?
foreign_key_exists?(:ci_builds_runner_session, :p_ci_builds, name: :fk_rails_70707857d3_p)
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class ReplaceOldFkCiPendingBuildsToBuilds < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
def up
return unless should_run?
return if new_foreign_key_exists?
with_lock_retries do
remove_foreign_key_if_exists :ci_pending_builds, :ci_builds,
name: :fk_rails_725a2644a3_p, reverse_lock_order: true
rename_constraint :ci_pending_builds, :temp_fk_rails_725a2644a3_p, :fk_rails_725a2644a3_p
end
end
def down
return unless should_run?
return unless new_foreign_key_exists?
add_concurrent_foreign_key :ci_pending_builds, :ci_builds,
name: :temp_fk_rails_725a2644a3_p,
column: [:partition_id, :build_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :cascade,
validate: true,
reverse_lock_order: true
switch_constraint_names :ci_pending_builds, :fk_rails_725a2644a3_p, :temp_fk_rails_725a2644a3_p
end
private
def should_run?
can_execute_on?(:ci_pending_builds, :ci_builds)
end
def new_foreign_key_exists?
foreign_key_exists?(:ci_pending_builds, :p_ci_builds, name: :fk_rails_725a2644a3_p)
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class ReplaceOldFkCiBuildTraceMetadataToBuilds < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
def up
return unless should_run?
return if new_foreign_key_exists?
with_lock_retries do
remove_foreign_key_if_exists :ci_build_trace_metadata, :ci_builds,
name: :fk_rails_aebc78111f_p, reverse_lock_order: true
rename_constraint :ci_build_trace_metadata, :temp_fk_rails_aebc78111f_p, :fk_rails_aebc78111f_p
end
end
def down
return unless should_run?
return unless new_foreign_key_exists?
add_concurrent_foreign_key :ci_build_trace_metadata, :ci_builds,
name: :temp_fk_rails_aebc78111f_p,
column: [:partition_id, :build_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :cascade,
validate: true,
reverse_lock_order: true
switch_constraint_names :ci_build_trace_metadata, :fk_rails_aebc78111f_p, :temp_fk_rails_aebc78111f_p
end
private
def should_run?
can_execute_on?(:ci_build_trace_metadata, :ci_builds)
end
def new_foreign_key_exists?
foreign_key_exists?(:ci_build_trace_metadata, :p_ci_builds, name: :fk_rails_aebc78111f_p)
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class PrepareIndexIssuesOnProjectHealthStatusAscWorkItemType < Gitlab::Database::Migration[2.1]
INDEX_NAME = 'index_issues_on_project_health_status_asc_work_item_type'
def up
prepare_async_index :issues,
[:project_id, :health_status, :id, :state_id, :work_item_type_id],
order: { health_status: 'ASC NULLS LAST', id: :desc },
name: INDEX_NAME
end
def down
unprepare_async_index :issues, INDEX_NAME
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class PrepareIndexIssuesOnProjectHealthStatusDescWorkItemType < Gitlab::Database::Migration[2.1]
INDEX_NAME = 'index_issues_on_project_health_status_desc_work_item_type'
def up
prepare_async_index :issues,
[:project_id, :health_status, :id, :state_id, :work_item_type_id],
order: { health_status: 'DESC NULLS LAST', id: :desc },
name: INDEX_NAME
end
def down
unprepare_async_index :issues, INDEX_NAME
end
end

View File

@ -0,0 +1 @@
a65e4718899a9339ef47da715f1fe48e20142b634bb95b0db70d2e4b6c5cc103

View File

@ -0,0 +1 @@
e5ba43d180f8761c1892541ab4ca38c178bd3768f5ca0b9cfa977309306d600f

View File

@ -0,0 +1 @@
96ad4cbf0debff7b289fa59b4af6164cd06226972ba55fbe696ad0e4c73eea0e

View File

@ -0,0 +1 @@
c9e40d4c7d14b0914d520a8b425a8805e25e43beaea9b9a21a76e94c77431f79

View File

@ -0,0 +1 @@
694a5369b0d8536402e0fe1091413bd20f978035fa0f2ba0487d5287ffc6fe19

View File

@ -0,0 +1 @@
c871540f13d8d0ee7945ff1dd625ace59ec2b1f0907701ba7ff62d411bd51dac

View File

@ -0,0 +1 @@
5eb86fcc023323c515964827c44052b5b6d8b6b216b3ccbde8b2e2033fd72180

View File

@ -0,0 +1 @@
86407b35a175581e639e9e7d537e5655a80cf998f7c46e17b011b4f0241ffd5c

View File

@ -0,0 +1 @@
0bfa09f5e8290de7dc2931b1ef6f5259653619cb4a722a04dc58c3c98eb523e4

View File

@ -0,0 +1 @@
c18fe1c9744ebb939972f561f1de88afafedd0bba61b190d5fce4df25bd2d9f7

View File

@ -0,0 +1 @@
99ecaa37e4b47b15fafb593e9af6bcf37f393dd10ecfdbd11a196c4700ab9571

View File

@ -0,0 +1 @@
eb2627c95cf579d4c8a0eaa239093b0b3badbf666c257425bcb5d5a47cbf7d87

View File

@ -35642,7 +35642,7 @@ ALTER TABLE ONLY requirements
ADD CONSTRAINT fk_85044baef0 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_pending_states
ADD CONSTRAINT fk_861cd17da3_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ADD CONSTRAINT fk_861cd17da3_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY geo_event_log
ADD CONSTRAINT fk_86c84214ec FOREIGN KEY (repository_renamed_event_id) REFERENCES geo_repository_renamed_events(id) ON DELETE CASCADE;
@ -35666,7 +35666,7 @@ ALTER TABLE ONLY issues
ADD CONSTRAINT fk_899c8f3231 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_trace_chunks
ADD CONSTRAINT fk_89e29fa5ee_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ADD CONSTRAINT fk_89e29fa5ee_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY sbom_vulnerable_component_versions
ADD CONSTRAINT fk_8a2a1197f9 FOREIGN KEY (sbom_component_version_id) REFERENCES sbom_component_versions(id) ON DELETE CASCADE;
@ -35735,7 +35735,7 @@ ALTER TABLE ONLY user_group_callouts
ADD CONSTRAINT fk_9dc8b9d4b2 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_unit_test_failures
ADD CONSTRAINT fk_9e0fc58930_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ADD CONSTRAINT fk_9e0fc58930_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY protected_environments
ADD CONSTRAINT fk_9e112565b7 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
@ -35894,7 +35894,7 @@ ALTER TABLE ONLY snippets
ADD CONSTRAINT fk_be41fd4bb7 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_sources_pipelines
ADD CONSTRAINT fk_be5624bf37_p FOREIGN KEY (source_partition_id, source_job_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ADD CONSTRAINT fk_be5624bf37_p FOREIGN KEY (source_partition_id, source_job_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY packages_maven_metadata
ADD CONSTRAINT fk_be88aed360 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
@ -36053,7 +36053,7 @@ ALTER TABLE ONLY approval_project_rules
ADD CONSTRAINT fk_e1372c912e FOREIGN KEY (scan_result_policy_id) REFERENCES scan_result_policies(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_resources
ADD CONSTRAINT fk_e169a8e3d5_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE SET NULL;
ADD CONSTRAINT fk_e169a8e3d5_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE SET NULL;
ALTER TABLE ONLY ci_sources_pipelines
ADD CONSTRAINT fk_e1bad85861 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
@ -36347,7 +36347,7 @@ ALTER TABLE ONLY users_security_dashboard_projects
ADD CONSTRAINT fk_rails_150cd5682c FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_report_results
ADD CONSTRAINT fk_rails_16cb1ff064_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ADD CONSTRAINT fk_rails_16cb1ff064_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY catalog_resources
ADD CONSTRAINT fk_rails_16f09e5c44 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@ -36605,7 +36605,7 @@ ALTER TABLE ONLY chat_teams
ADD CONSTRAINT fk_rails_3b543909cb FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_needs
ADD CONSTRAINT fk_rails_3cf221d4ed_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ADD CONSTRAINT fk_rails_3cf221d4ed_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY cluster_groups
ADD CONSTRAINT fk_rails_3d28377556 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
@ -36959,7 +36959,7 @@ ALTER TABLE ONLY analytics_dashboards_pointers
ADD CONSTRAINT fk_rails_7027b7eaa9 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_builds_runner_session
ADD CONSTRAINT fk_rails_70707857d3_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ADD CONSTRAINT fk_rails_70707857d3_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY list_user_preferences
ADD CONSTRAINT fk_rails_70b2ef5ce2 FOREIGN KEY (list_id) REFERENCES lists(id) ON DELETE CASCADE;
@ -36968,7 +36968,7 @@ ALTER TABLE ONLY project_custom_attributes
ADD CONSTRAINT fk_rails_719c3dccc5 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_pending_builds
ADD CONSTRAINT fk_rails_725a2644a3_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ADD CONSTRAINT fk_rails_725a2644a3_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE security_findings
ADD CONSTRAINT fk_rails_729b763a54 FOREIGN KEY (scanner_id) REFERENCES vulnerability_scanners(id) ON DELETE CASCADE;
@ -37370,7 +37370,7 @@ ALTER TABLE ONLY metrics_dashboard_annotations
ADD CONSTRAINT fk_rails_aeb11a7643 FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_trace_metadata
ADD CONSTRAINT fk_rails_aebc78111f_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ADD CONSTRAINT fk_rails_aebc78111f_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY bulk_import_trackers
ADD CONSTRAINT fk_rails_aed566d3f3 FOREIGN KEY (bulk_import_entity_id) REFERENCES bulk_import_entities(id) ON DELETE CASCADE;
@ -37942,39 +37942,9 @@ ALTER TABLE issue_search_data
ALTER TABLE product_analytics_events_experimental
ADD CONSTRAINT product_analytics_events_experimental_project_id_fkey FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_pending_states
ADD CONSTRAINT temp_fk_861cd17da3_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_trace_chunks
ADD CONSTRAINT temp_fk_89e29fa5ee_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY ci_unit_test_failures
ADD CONSTRAINT temp_fk_9e0fc58930_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY ci_sources_pipelines
ADD CONSTRAINT temp_fk_be5624bf37_p FOREIGN KEY (source_partition_id, source_job_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY ci_resources
ADD CONSTRAINT temp_fk_e169a8e3d5_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE SET NULL;
ALTER TABLE ONLY ci_builds_metadata
ADD CONSTRAINT temp_fk_e20479742e_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID;
ALTER TABLE ONLY ci_build_report_results
ADD CONSTRAINT temp_fk_rails_16cb1ff064_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_needs
ADD CONSTRAINT temp_fk_rails_3cf221d4ed_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY ci_builds_runner_session
ADD CONSTRAINT temp_fk_rails_70707857d3_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY ci_pending_builds
ADD CONSTRAINT temp_fk_rails_725a2644a3_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_trace_metadata
ADD CONSTRAINT temp_fk_rails_aebc78111f_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY ci_job_artifacts
ADD CONSTRAINT temp_fk_rails_c5137cb2c1_p FOREIGN KEY (partition_id, job_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID;

View File

@ -16,7 +16,7 @@ Review apps:
- Provide an automatic live preview of changes made in a feature branch by spinning up a dynamic environment for your merge requests.
- Allow designers and product managers to see your changes without needing to check out your branch and run your changes in a sandbox environment.
- Are fully integrated with the [GitLab DevOps LifeCycle](../../index.md#the-entire-devops-lifecycle).
- Are fully integrated with the [GitLab DevOps LifeCycle](https://about.gitlab.com/stages-devops-lifecycle/).
- Allow you to deploy your changes wherever you want.
![review apps workflow](img/continuous-delivery-review-apps.svg)

View File

@ -56,44 +56,66 @@ in the directory `lib/gitlab/background_migration/`.
### Execution mechanism
After being enqueued, batched background migrations are picked in sequential order by a Sidekiq worker.
At this point, batched jobs are created - or retried and iterate over a range of records that are fetched based
on the configuration defined inside the migration class. Each job instance receives a set of
arguments based on this migration configuration. After each job execution, the optimization mechanism takes
place to decide if the migration can be optimized.
Batched background migrations are picked from the queue in the order they are enqueued. Multiple migrations are fetched
and executed in parallel, as long they are in active state and do not target the same database table.
The default number of migrations processed in parallel is 2, for GitLab.com this limit is configured to 4.
Once migration is picked for execution, a job is created for the specific batch. After each job execution, migration's
batch size may be increased or decreased, based on the performance of the last 20 jobs.
```plantuml
@startuml
hide empty description
skinparam ConditionEndStyle hline
start
rectangle Queue {
:Migration Three;
note right: Waiting for Migration Two
:Migration Two;
note right: Waiting for Migration One
left to right direction
rectangle "Batched Background Migration Queue" as migrations {
rectangle "Migration N (active)" as migrationn
rectangle "Migration 1 (completed)" as migration1
rectangle "Migration 2 (active)" as migration2
rectangle "Migration 3 (on hold)" as migration3
rectangle "Migration 4 (active)" as migration4
migration1 -[hidden]> migration2
migration2 -[hidden]> migration3
migration3 -[hidden]> migration4
migration4 -[hidden]> migrationn
}
rectangle "Execution Workers" as workers {
rectangle "Execution Worker 1 (busy)" as worker1
rectangle "Execution Worker 2 (available)" as worker2
worker1 -[hidden]> worker2
}
migration2 --> [Scheduling Worker]
migration4 --> [Scheduling Worker]
[Scheduling Worker] --> worker2
@enduml
```
Soon as a worker is available, the BBM is processed by the runner.
```plantuml
@startuml
hide empty description
start
rectangle Runner {
:Migration One;
note right: Active migration
if (Still have records to migrate?) then (Yes)
if (Have a pending batched job?) then (Yes)
:Retry batched job;
:Migration;
if (Have reached batching bounds?) then (Yes)
if (Have jobs to retry?) then (Yes)
:Fetch the batched job;
else (No)
:Create a batched job;
endif
:Evaluate DB health;
note right: Table autovacuum, Patroni Apdex, Write-ahead logging
if (Evaluation signs to stop?) then (Yes)
:Put migration on hold;
else (No)
:Optimize migration;
:Finish active migration;
stop
endif
else (No)
:Finish active migration;
:Create a batched job;
endif
:Execute batched job;
:Evaluate DB health;
note right: Checks for table autovacuum, Patroni Apdex, Write-ahead logging
if (Evaluation signs to stop?) then (Yes)
:Put migration on hold;
else (No)
:Optimize migration;
endif
}
stop
@enduml
```
@ -161,13 +183,16 @@ the migration as `failed`) if any of the following is true:
Because batched migrations are update heavy and there were few incidents in the past because of the heavy load from migrations while the database was underperforming, a throttling mechanism exists to mitigate them.
These database indicators are checked to throttle a migration. On getting a stop signal, the migration is paused for a set time (10 minutes):
These database indicators are checked to throttle a migration. On getting a
stop signal, the migration is paused for a set time (10 minutes):
- WAL queue pending archival crossing a threshold.
- Active autovacuum on the tables on which the migration works on.
- Patroni apdex SLI dropping below the SLO.
It's an ongoing effort to add more indicators to further enhance the database health check framework, more details can be found in this [epic](https://gitlab.com/groups/gitlab-org/-/epics/7594).
It's an ongoing effort to add more indicators to further enhance the
database health check framework. For more details, see
[epic 7594](https://gitlab.com/groups/gitlab-org/-/epics/7594).
### Isolation
@ -348,21 +373,6 @@ class CopyColumnUsingBackgroundMigrationJob < BatchedMigrationJob
end
```
## Throttling batched migrations
Because batched migrations are update heavy and there were few incidents in the past because of the heavy load from migrations while the database was underperforming, a throttling mechanism exists to mitigate them.
These database indicators are checked to throttle a migration. On getting a
stop signal, the migration is paused for a set time (10 minutes):
- WAL queue pending archival crossing a threshold.
- Active autovacuum on the tables on which the migration works on.
- Patroni apdex SLI dropping below the SLO.
It's an ongoing effort to add more indicators to further enhance the
database health check framework. For more details, see
[epic 7594](https://gitlab.com/groups/gitlab-org/-/epics/7594).
## Additional filters
By default, when creating background jobs to perform the migration, batched background migrations

View File

@ -19,16 +19,15 @@ description: 'Learn how to use and administer GitLab, the most scalable Git-base
# GitLab Docs
Welcome to the GitLab documentation!
Explore the different areas of the documentation:
| | |
|:------------------------|:------------------------|
| [**Use GitLab**](user/index.md)<br>Get started with GitLab features and functionality. | [**Administer GitLab**](administration/index.md)<br/>Administer a self-managed GitLab instance. |
| [**New to Git and GitLab?**](tutorials/index.md)<br/>Start learning about Git and GitLab. | [**Contribute to GitLab development**](#contributing-to-gitlab)<br/>Create new GitLab functionality and documentation. |
| [**New to Git and GitLab?**](tutorials/index.md)<br/>Start learning about Git and GitLab. | [**Contribute to GitLab development**](#contribute-to-gitlab)<br/>Create new GitLab functionality and documentation. |
| [**Coming to GitLab from another platform?**](#coming-to-gitlab-from-another-platform)<br/>Learn how to move to GitLab. | [**Build an integration with GitLab**](#build-an-integration-with-gitlab)<br/>Integrate with Jira and other common applications. |
| [**Choose a subscription**](subscriptions/index.md)<br/>Determine which subscription tier makes sense for you. | [**Install GitLab**](https://about.gitlab.com/install/)<br/>Install GitLab on different platforms. |
| [**Choose a subscription**](subscriptions/index.md)<br/>Determine which subscription tier makes sense for you. | [**Install GitLab**](install/index.md)<br/>Install GitLab on different platforms. |
| [**Reference architectures**](administration/reference_architectures/index.md)<br/>View recommended deployments at scale. | [**Upgrade GitLab**](update/index.md)<br/>Upgrade your GitLab self-managed instance to the latest version. |
| [**GitLab releases**](https://about.gitlab.com/releases/)<br/>See what's new in GitLab. | |
## Popular topics
@ -48,15 +47,7 @@ Have a look at some of our most popular topics:
| [SSL configuration for Linux package installations](https://docs.gitlab.com/omnibus/settings/ssl/index.html) | SSL settings for self-managed instances installed using Linux packages. |
| [GitLab.com settings](user/gitlab_com/index.md) | Settings used for GitLab.com. |
## The entire DevOps lifecycle
GitLab is the first single application for software development, security,
and operations that enables [Concurrent DevOps](https://about.gitlab.com/topics/concurrent-devops/).
GitLab makes the software lifecycle faster and radically improves the speed of business.
GitLab provides solutions for [each of the stages of the DevOps lifecycle](https://about.gitlab.com/stages-devops-lifecycle/).
### User account
## User account
For more information about GitLab account management, see:
@ -78,7 +69,7 @@ If you are coming to GitLab from another platform, the following information is
## Build an integration with GitLab
There are many ways to integrate with GitLab, including:
You can build integrations with GitLab:
| Topic | Description |
|:-------------------------------------------|:------------|
@ -86,15 +77,11 @@ There are many ways to integrate with GitLab, including:
| [GitLab GraphQL API](api/graphql/index.md) | Integrate with GitLab using our GraphQL API. |
| [Integrations](integration/index.md) | Integrations with third-party products. |
## Contributing to GitLab
GitLab Community Edition is [open source](https://gitlab.com/gitlab-org/gitlab-foss/)
and GitLab Enterprise Edition is [open-core](https://gitlab.com/gitlab-org/gitlab/).
## Contribute to GitLab
Learn how to contribute to GitLab with the following resources:
| Topic | Description |
|:------------------------------------------------------------|:------------|
| [Development](development/index.md) | How to contribute to GitLab development. |
| [Legal](legal/index.md) | Contributor license agreements. |
| [Writing documentation](development/documentation/index.md) | How to contribute to GitLab Docs. |
| [Contribute to GitLab development](development/index.md). | How to contribute to GitLab development. |
| [Contribute to the documentation](development/documentation/index.md) | How to contribute to GitLab documentation. |

View File

@ -262,6 +262,16 @@ module Gitlab
SQL
end
def switch_constraint_names(table_name, constraint_a, constraint_b)
validate_not_in_transaction!(:switch_constraint_names)
with_lock_retries do
rename_constraint(table_name, constraint_a, :temp_name_for_renaming)
rename_constraint(table_name, constraint_b, constraint_a)
rename_constraint(table_name, :temp_name_for_renaming, constraint_b)
end
end
def validate_check_constraint_name!(constraint_name)
return unless constraint_name.to_s.length > MAX_IDENTIFIER_NAME_LENGTH

View File

@ -518,13 +518,15 @@ module Gitlab
empty_diff_stats
end
def find_changed_paths(commits)
def find_changed_paths(commits, merge_commit_diff_mode: nil)
processed_commits = commits.reject { |ref| ref.blank? || Gitlab::Git.blank_ref?(ref) }
return [] if processed_commits.empty?
wrapped_gitaly_errors do
gitaly_commit_client.find_changed_paths(processed_commits)
gitaly_commit_client.find_changed_paths(
processed_commits, merge_commit_diff_mode: merge_commit_diff_mode
)
end
rescue CommandError, TypeError, NoRepository
[]

View File

@ -12,6 +12,11 @@ module Gitlab
'unspecified' => Gitaly::CommitDiffRequest::WhitespaceChanges::WHITESPACE_CHANGES_UNSPECIFIED
}.freeze
MERGE_COMMIT_DIFF_MODES = {
all_parents: Gitaly::FindChangedPathsRequest::MergeCommitDiffMode::MERGE_COMMIT_DIFF_MODE_ALL_PARENTS,
include_merges: Gitaly::FindChangedPathsRequest::MergeCommitDiffMode::MERGE_COMMIT_DIFF_MODE_INCLUDE_MERGES
}.freeze
TREE_ENTRIES_DEFAULT_LIMIT = 100_000
def initialize(repository)
@ -242,8 +247,35 @@ module Gitlab
response.flat_map { |rsp| rsp.stats.to_a }
end
def find_changed_paths(commits)
request = find_changed_paths_request(commits)
# When finding changed paths and passing a sha for a merge commit we can
# specify how to diff the commit.
#
# When diffing a merge commit and merge_commit_diff_mode is :all_parents
# file paths are only returned if changed in both parents (or all parents
# if diffing an octopus merge)
#
# This means if we create a merge request that includes a merge commit
# of changes already existing in the target branch, we can omit those
# changes when looking up the changed paths.
#
# e.g.
# 1. User branches from master to new branch named feature/foo_bar
# 2. User changes ./foo_bar.rb and commits change to feature/foo_bar
# 3. Another user merges a change to ./bar_baz.rb to master
# 4. User merges master into feature/foo_bar
# 5. User pushes to GitLab
# 6. GitLab checks which files have changed
#
# case merge_commit_diff_mode
# when :all_parents
# ['foo_bar.rb']
# when :include_merges
# ['foo_bar.rb', 'bar_baz.rb'],
# else # defaults to :include_merges behavior
# ['foo_bar.rb', 'bar_baz.rb'],
#
def find_changed_paths(commits, merge_commit_diff_mode: nil)
request = find_changed_paths_request(commits, merge_commit_diff_mode)
response = gitaly_client_call(@repository.storage, :diff_service, :find_changed_paths, request, timeout: GitalyClient.medium_timeout)
response.flat_map do |msg|
@ -606,9 +638,11 @@ module Gitlab
response.commit
end
def find_changed_paths_request(commits)
def find_changed_paths_request(commits, merge_commit_diff_mode)
diff_mode = MERGE_COMMIT_DIFF_MODES[merge_commit_diff_mode] if Feature.enabled?(:merge_commit_diff_modes)
if Feature.disabled?(:find_changed_paths_new_format)
return Gitaly::FindChangedPathsRequest.new(repository: @gitaly_repo, commits: commits)
return Gitaly::FindChangedPathsRequest.new(repository: @gitaly_repo, commits: commits, merge_commit_diff_mode: diff_mode)
end
commit_requests = commits.map do |commit|
@ -617,7 +651,7 @@ module Gitlab
)
end
Gitaly::FindChangedPathsRequest.new(repository: @gitaly_repo, requests: commit_requests)
Gitaly::FindChangedPathsRequest.new(repository: @gitaly_repo, requests: commit_requests, merge_commit_diff_mode: diff_mode)
end
def path_error_message(path_error)

View File

@ -31,6 +31,11 @@ class TmpUser < ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord
self.table_name = 'users'
add_authentication_token_field :incoming_email_token, token_generator: -> { SecureRandom.hex.to_i(16).to_s(36) }
add_authentication_token_field :feed_token
add_authentication_token_field :incoming_email_token,
token_generator: -> { User.generate_incoming_mail_token }
add_authentication_token_field :feed_token, format_with_prefix: :prefix_for_feed_token
def prefix_for_feed_token
User::FEED_TOKEN_PREFIX
end
end

View File

@ -27688,6 +27688,9 @@ msgstr ""
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Map data from"
msgstr ""
msgid "Mar"
msgstr ""
@ -46014,6 +46017,9 @@ msgstr ""
msgid "The list creation wizard is already open"
msgstr ""
msgid "The map can not be displayed because there was an error loading the GeoJSON file."
msgstr ""
msgid "The maximum amount of time users have to set up two-factor authentication before it's enforced."
msgstr ""

View File

@ -156,6 +156,7 @@
"js-yaml": "^3.13.1",
"jszip": "^3.1.3",
"katex": "^0.13.2",
"leaflet": "^1.9.4",
"lodash": "^4.17.20",
"lowlight": "^2.9.0",
"marked": "^4.0.18",

View File

@ -33,7 +33,7 @@ module Gitlab
div :project
div :storage_type_legend
span :container_registry_size
div :purchased_usage_total
div :storage_purchased, 'data-testid': 'storage-purchased'
div :storage_purchase_successful_alert, text: /You have successfully purchased a storage/
# Pending members
@ -70,7 +70,7 @@ module Gitlab
def total_purchased_storage
::QA::Support::WaitForRequests.wait_for_requests
purchased_usage_total[/(\d+){2}.\d+/].to_f
storage_purchased[/(\d+){2}.\d+/].to_f
end
# Waits for additional CI minutes to be available on the page

View File

@ -533,51 +533,27 @@ module Gitlab
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :purchased_usage_total_free+
# @return [String] The text content or value of +purchased_usage_total_free+
def purchased_usage_total_free
# @note Defined as +div :storage_purchased+
# @return [String] The text content or value of +storage_purchased+
def storage_purchased
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.purchased_usage_total_free_element).to exist
# expect(usage_quotas.storage_purchased_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def purchased_usage_total_free_element
def storage_purchased_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_purchased_usage_total_free
# expect(usage_quotas).to be_storage_purchased
# end
# @return [Boolean] true if the +purchased_usage_total_free+ element is present on the page
def purchased_usage_total_free?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +span :purchased_usage_total+
# @return [String] The text content or value of +purchased_usage_total+
def purchased_usage_total
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.purchased_usage_total_element).to exist
# end
# @return [Watir::Span] The raw +Span+ element
def purchased_usage_total_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_purchased_usage_total
# end
# @return [Boolean] true if the +purchased_usage_total+ element is present on the page
def purchased_usage_total?
# @return [Boolean] true if the +storage_purchased+ element is present on the page
def storage_purchased?
# This is a stub, used for indexing. The method is dynamically generated.
end

View File

@ -53,7 +53,7 @@ exports[`Design note component should match the snapshot 1`] = `
/>
<gllink-stub
class="note-timestamp system-note-separator gl-display-block gl-mb-2"
class="note-timestamp system-note-separator gl-display-block gl-mb-2 gl-font-sm"
href="#note_123"
>
<timeagotooltip-stub

View File

@ -26,6 +26,8 @@ describe('containsSensitiveToken', () => {
'token: glpat-cgyKc1k_AsnEpmP-5fRL',
'token: GlPat-abcdefghijklmnopqrstuvwxyz',
'token: feed_token=ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'token: feed_token=glft-ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'token: feed_token=glft-a8cc74ccb0de004d09a968705ba49099229b288b3de43f26c473a9d8d7fb7693-1234',
'https://example.com/feed?feed_token=123456789_abcdefghij',
'glpat-1234567890 and feed_token=ABCDEFGHIJKLMNOPQRSTUVWXYZ',
];

View File

@ -0,0 +1,40 @@
import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import GeoJsonViewer from '~/repository/components/blob_viewers/geo_json/geo_json_viewer.vue';
import { initLeafletMap } from '~/repository/components/blob_viewers/geo_json/utils';
import { RENDER_ERROR_MSG } from '~/repository/components/blob_viewers/geo_json/constants';
import { createAlert } from '~/alert';
jest.mock('~/repository/components/blob_viewers/geo_json/utils');
jest.mock('~/alert');
describe('GeoJson Viewer', () => {
let wrapper;
const GEO_JSON_MOCK_DATA = '{ "type": "FeatureCollection" }';
const createComponent = (rawTextBlob = GEO_JSON_MOCK_DATA) => {
wrapper = shallowMountExtended(GeoJsonViewer, {
propsData: { blob: { rawTextBlob } },
});
};
beforeEach(() => createComponent());
const findMapWrapper = () => wrapper.findByTestId('map');
it('calls a the initLeafletMap util', () => {
const mapWrapper = findMapWrapper();
expect(initLeafletMap).toHaveBeenCalledWith(mapWrapper.element, JSON.parse(GEO_JSON_MOCK_DATA));
expect(mapWrapper.exists()).toBe(true);
});
it('displays an error if invalid json is provided', async () => {
createComponent('invalid JSON');
await nextTick();
expect(createAlert).toHaveBeenCalledWith({ message: RENDER_ERROR_MSG });
expect(findMapWrapper().exists()).toBe(false);
});
});

View File

@ -0,0 +1,68 @@
import { map, tileLayer, geoJson, featureGroup, Icon } from 'leaflet';
import * as utils from '~/repository/components/blob_viewers/geo_json/utils';
import {
OPEN_STREET_TILE_URL,
MAP_ATTRIBUTION,
OPEN_STREET_COPYRIGHT_LINK,
ICON_CONFIG,
} from '~/repository/components/blob_viewers/geo_json/constants';
jest.mock('leaflet', () => ({
featureGroup: () => ({ getBounds: jest.fn() }),
Icon: { Default: { mergeOptions: jest.fn() } },
tileLayer: jest.fn(),
map: jest.fn().mockReturnValue({ fitBounds: jest.fn() }),
geoJson: jest.fn().mockReturnValue({ addTo: jest.fn() }),
}));
describe('GeoJson utilities', () => {
const mockWrapper = document.createElement('div');
const mockData = { test: 'data' };
describe('initLeafletMap', () => {
describe('valid params', () => {
beforeEach(() => utils.initLeafletMap(mockWrapper, mockData));
it('sets the correct icon', () => {
expect(Icon.Default.mergeOptions).toHaveBeenCalledWith(ICON_CONFIG);
});
it('inits the leaflet map', () => {
const attribution = `${MAP_ATTRIBUTION} ${OPEN_STREET_COPYRIGHT_LINK}`;
expect(tileLayer).toHaveBeenCalledWith(OPEN_STREET_TILE_URL, { attribution });
expect(map).toHaveBeenCalledWith(mockWrapper, { layers: [] });
});
it('adds geojson data to the leaflet map', () => {
expect(geoJson().addTo).toHaveBeenCalledWith(map());
});
it('fits the map to the correct bounds', () => {
expect(map().fitBounds).toHaveBeenCalledWith(featureGroup().getBounds());
});
it('generates popup content containing the metaData', () => {
const popupContent = utils.popupContent(mockData);
expect(popupContent).toContain(Object.keys(mockData)[0]);
expect(popupContent).toContain(mockData.test);
});
});
describe('invalid params', () => {
it.each([
[null, null],
[null, mockData],
[mockWrapper, null],
])('does nothing (returns early) if any of the params are not provided', (wrapper, data) => {
utils.initLeafletMap(wrapper, data);
expect(Icon.Default.mergeOptions).not.toHaveBeenCalled();
expect(tileLayer).not.toHaveBeenCalled();
expect(map).not.toHaveBeenCalled();
expect(geoJson().addTo).not.toHaveBeenCalled();
expect(map().fitBounds).not.toHaveBeenCalled();
});
});
});
});

View File

@ -679,4 +679,43 @@ RSpec.describe Gitlab::Database::Migrations::ConstraintsHelpers do
end
end
end
describe '#switch_constraint_names' do
before do
ActiveRecord::Migration.connection.create_table(:_test_table) do |t|
t.references :supplier, foreign_key: { to_table: :_test_table, name: :supplier_fk }
t.references :customer, foreign_key: { to_table: :_test_table, name: :customer_fk }
end
end
context 'when inside a transaction' do
it 'raises an error' do
expect(model).to receive(:transaction_open?).and_return(true)
expect do
model.switch_constraint_names(:_test_table, :supplier_fk, :customer_fk)
end.to raise_error(RuntimeError)
end
end
context 'when outside a transaction' do
before do
allow(model).to receive(:transaction_open?).and_return(false)
end
it 'executes the statement to swap the constraint names' do
expect { model.switch_constraint_names(:_test_table, :supplier_fk, :customer_fk) }
.to change { constrained_column_for(:customer_fk) }.from(:customer_id).to(:supplier_id)
.and change { constrained_column_for(:supplier_fk) }.from(:supplier_id).to(:customer_id)
end
def constrained_column_for(fk_name)
Gitlab::Database::PostgresForeignKey
.find_by!(referenced_table_name: :_test_table, name: fk_name)
.constrained_columns
.first
.to_sym
end
end
end
end

View File

@ -1454,7 +1454,7 @@ RSpec.describe Gitlab::Git::Repository, feature_category: :source_code_managemen
it "returns the number of commits in the whole repository" do
options = { all: true }
expect(repository.count_commits(options)).to eq(316)
expect(repository.count_commits(options)).to eq(322)
end
end
@ -1675,6 +1675,41 @@ RSpec.describe Gitlab::Git::Repository, feature_category: :source_code_managemen
expect(collection).to be_a(Enumerable)
expect(collection.to_a).to be_empty
end
describe 'merge_commit_diff_mode argument' do
let(:gitaly_commit_client) { double('Gitlab::GitalyClient::CommitService') }
before do
allow(repository).to receive(:gitaly_commit_client).and_return(gitaly_commit_client)
allow(gitaly_commit_client).to receive(:find_changed_paths)
end
context 'when omitted' do
before do
repository.find_changed_paths(['sha'])
end
it 'defaults to nil' do
expect(gitaly_commit_client)
.to have_received(:find_changed_paths)
.with(['sha'], merge_commit_diff_mode: nil)
end
end
context 'when included' do
let(:passed_value) { 'foobar' }
before do
repository.find_changed_paths(['sha'], merge_commit_diff_mode: passed_value)
end
it 'passes the value on to the commit client' do
expect(gitaly_commit_client)
.to have_received(:find_changed_paths)
.with(['sha'], merge_commit_diff_mode: passed_value)
end
end
end
end
describe "#ls_files" do

View File

@ -168,34 +168,171 @@ RSpec.describe Gitlab::GitalyClient::CommitService, feature_category: :gitaly do
end
describe '#find_changed_paths' do
let(:commits) { %w[1a0b36b3cdad1d2ee32457c102a8c0b7056fa863 cfe32cf61b73a0d5e9f13e774abde7ff789b1660] }
let(:requests) do
[
Gitaly::FindChangedPathsRequest::Request.new(
commit_request: Gitaly::FindChangedPathsRequest::Request::CommitRequest.new(commit_revision: '1a0b36b3cdad1d2ee32457c102a8c0b7056fa863')
),
Gitaly::FindChangedPathsRequest::Request.new(
commit_request: Gitaly::FindChangedPathsRequest::Request::CommitRequest.new(commit_revision: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660')
)
let(:mapped_merge_commit_diff_mode) { described_class::MERGE_COMMIT_DIFF_MODES[merge_commit_diff_mode] }
let(:commits) do
%w[
ade1c0b4b116209ed2a9958436b26f89085ec383
594937c22df7a093888ff13af518f2b683f5f719
760c58db5a6f3b64ad7e3ff6b3c4a009da7d9b33
2b298117a741cdb06eb48df2c33f1390cf89f7e8
c41e12c387b4e0e41bfc17208252d6a6430f2fcd
1ada92f78a19f27cb442a0a205f1c451a3a15432
]
end
it 'sends an RPC request and returns the stats' do
request = Gitaly::FindChangedPathsRequest.new(repository: repository_message, requests: requests)
let(:requests) do
commits.map do |commit|
Gitaly::FindChangedPathsRequest::Request.new(
commit_request: Gitaly::FindChangedPathsRequest::Request::CommitRequest.new(commit_revision: commit)
)
end
end
changed_paths_response = Gitaly::FindChangedPathsResponse.new(
paths: [{
path: "app/assets/javascripts/boards/components/project_select.vue",
status: :MODIFIED
}])
let(:request) do
Gitaly::FindChangedPathsRequest.new(repository: repository_message, requests: requests, merge_commit_diff_mode: merge_commit_diff_mode)
end
expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:find_changed_paths)
.with(request, kind_of(Hash)).and_return([changed_paths_response])
subject { described_class.new(repository).find_changed_paths(commits, merge_commit_diff_mode: merge_commit_diff_mode).as_json }
returned_value = described_class.new(repository).find_changed_paths(commits)
mapped_expected_value = changed_paths_response.paths.map { |path| Gitlab::Git::ChangedPath.new(status: path.status, path: path.path) }
before do
allow(Gitaly::FindChangedPathsRequest).to receive(:new).and_call_original
end
expect(returned_value.as_json).to eq(mapped_expected_value.as_json)
shared_examples 'includes paths different in any parent' do
let(:changed_paths) do
[
{ path: 'files/locked/foo.lfs', status: 'ADDED' },
{ path: 'files/locked/foo.lfs', status: 'MODIFIED' },
{ path: 'files/locked/bar.lfs', status: 'ADDED' },
{ path: 'files/locked/foo.lfs', status: 'MODIFIED' },
{ path: 'files/locked/bar.lfs', status: 'ADDED' },
{ path: 'files/locked/bar.lfs', status: 'MODIFIED' },
{ path: 'files/locked/bar.lfs', status: 'MODIFIED' },
{ path: 'files/locked/baz.lfs', status: 'ADDED' },
{ path: 'files/locked/baz.lfs', status: 'ADDED' }
].as_json
end
it 'returns all paths, including ones from merge commits' do
is_expected.to eq(changed_paths)
end
end
shared_examples 'includes paths different in all parents' do
let(:changed_paths) do
[
{ path: 'files/locked/foo.lfs', status: 'ADDED' },
{ path: 'files/locked/foo.lfs', status: 'MODIFIED' },
{ path: 'files/locked/bar.lfs', status: 'ADDED' },
{ path: 'files/locked/bar.lfs', status: 'MODIFIED' },
{ path: 'files/locked/baz.lfs', status: 'ADDED' },
{ path: 'files/locked/baz.lfs', status: 'ADDED' }
].as_json
end
it 'returns only paths different in all parents' do
is_expected.to eq(changed_paths)
end
end
shared_examples 'uses requests format' do
it 'passes the revs via the requests kwarg as CommitRequest objects' do
subject
expect(Gitaly::FindChangedPathsRequest)
.to have_received(:new).with(
repository: repository_message,
requests: requests,
merge_commit_diff_mode: mapped_merge_commit_diff_mode
)
end
end
context 'when merge_commit_diff_mode is nil' do
let(:merge_commit_diff_mode) { nil }
include_examples 'includes paths different in any parent'
include_examples 'uses requests format'
end
context 'when merge_commit_diff_mode is :unspecified' do
let(:merge_commit_diff_mode) { :unspecified }
include_examples 'includes paths different in any parent'
include_examples 'uses requests format'
end
context 'when merge_commit_diff_mode is :include_merges' do
let(:merge_commit_diff_mode) { :include_merges }
include_examples 'includes paths different in any parent'
include_examples 'uses requests format'
end
context 'when merge_commit_diff_mode is invalid' do
let(:merge_commit_diff_mode) { 'invalid' }
include_examples 'includes paths different in any parent'
include_examples 'uses requests format'
end
context 'when merge_commit_diff_mode is :all_parents' do
let(:merge_commit_diff_mode) { :all_parents }
include_examples 'includes paths different in all parents'
include_examples 'uses requests format'
end
context 'when feature flag "merge_commit_diff_modes" is disabled' do
let(:mapped_merge_commit_diff_mode) { nil }
before do
stub_feature_flags(merge_commit_diff_modes: false)
end
context 'when merge_commit_diff_mode is nil' do
let(:merge_commit_diff_mode) { nil }
include_examples 'includes paths different in any parent'
include_examples 'uses requests format'
end
context 'when merge_commit_diff_mode is :unspecified' do
let(:merge_commit_diff_mode) { :unspecified }
include_examples 'includes paths different in any parent'
include_examples 'uses requests format'
end
context 'when merge_commit_diff_mode is :include_merges' do
let(:merge_commit_diff_mode) { :include_merges }
include_examples 'includes paths different in any parent'
include_examples 'uses requests format'
end
context 'when merge_commit_diff_mode is invalid' do
let(:merge_commit_diff_mode) { 'invalid' }
include_examples 'includes paths different in any parent'
include_examples 'uses requests format'
end
context 'when merge_commit_diff_mode is :all_parents' do
let(:merge_commit_diff_mode) { :all_parents }
include_examples 'includes paths different in any parent'
include_examples 'uses requests format'
end
end
context 'when feature flag "find_changed_paths_new_format" is disabled' do
@ -203,22 +340,104 @@ RSpec.describe Gitlab::GitalyClient::CommitService, feature_category: :gitaly do
stub_feature_flags(find_changed_paths_new_format: false)
end
it 'sends an RPC request and returns the stats' do
request = Gitaly::FindChangedPathsRequest.new(repository: repository_message, commits: commits)
shared_examples 'uses commits format' do
it do
subject
expect(Gitaly::FindChangedPathsRequest)
.to have_received(:new).with(
repository: repository_message,
commits: commits,
merge_commit_diff_mode: mapped_merge_commit_diff_mode
)
end
end
changed_paths_response = Gitaly::FindChangedPathsResponse.new(
paths: [{
path: "app/assets/javascripts/boards/components/project_select.vue",
status: :MODIFIED
}])
context 'when merge_commit_diff_mode is nil' do
let(:merge_commit_diff_mode) { nil }
expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:find_changed_paths)
.with(request, kind_of(Hash)).and_return([changed_paths_response])
include_examples 'includes paths different in any parent'
returned_value = described_class.new(repository).find_changed_paths(commits)
mapped_expected_value = changed_paths_response.paths.map { |path| Gitlab::Git::ChangedPath.new(status: path.status, path: path.path) }
include_examples 'uses commits format'
end
expect(returned_value.as_json).to eq(mapped_expected_value.as_json)
context 'when merge_commit_diff_mode is :unspecified' do
let(:merge_commit_diff_mode) { :unspecified }
include_examples 'includes paths different in any parent'
include_examples 'uses commits format'
end
context 'when merge_commit_diff_mode is :include_merges' do
let(:merge_commit_diff_mode) { :include_merges }
include_examples 'includes paths different in any parent'
include_examples 'uses commits format'
end
context 'when merge_commit_diff_mode is invalid' do
let(:merge_commit_diff_mode) { 'invalid' }
include_examples 'includes paths different in any parent'
include_examples 'uses commits format'
end
context 'when merge_commit_diff_mode is :all_parents' do
let(:merge_commit_diff_mode) { :all_parents }
include_examples 'includes paths different in all parents'
include_examples 'uses commits format'
end
context 'when feature flag "merge_commit_diff_modes" is disabled' do
let(:mapped_merge_commit_diff_mode) { nil }
before do
stub_feature_flags(merge_commit_diff_modes: false)
end
context 'when merge_commit_diff_mode is nil' do
let(:merge_commit_diff_mode) { nil }
include_examples 'includes paths different in any parent'
include_examples 'uses commits format'
end
context 'when merge_commit_diff_mode is :unspecified' do
let(:merge_commit_diff_mode) { :unspecified }
include_examples 'includes paths different in any parent'
include_examples 'uses commits format'
end
context 'when merge_commit_diff_mode is :include_merges' do
let(:merge_commit_diff_mode) { :include_merges }
include_examples 'includes paths different in any parent'
include_examples 'uses commits format'
end
context 'when merge_commit_diff_mode is invalid' do
let(:merge_commit_diff_mode) { 'invalid' }
include_examples 'includes paths different in any parent'
include_examples 'uses commits format'
end
context 'when merge_commit_diff_mode is :all_parents' do
let(:merge_commit_diff_mode) { :all_parents }
include_examples 'includes paths different in any parent'
include_examples 'uses commits format'
end
end
end
end

View File

@ -10,10 +10,11 @@ RSpec.describe Group, feature_category: :groups_and_projects do
describe 'associations' do
it { is_expected.to have_many :projects }
it { is_expected.to have_many(:all_group_members).dependent(:destroy) }
it { is_expected.to have_many(:group_members).dependent(:destroy) }
it { is_expected.to have_many(:namespace_members) }
it { is_expected.to have_many(:users).through(:group_members) }
it { is_expected.to have_many(:owners).through(:group_members) }
it { is_expected.to have_many(:owners).through(:all_group_members) }
it { is_expected.to have_many(:requesters).dependent(:destroy) }
it { is_expected.to have_many(:namespace_requesters) }
it { is_expected.to have_many(:members_and_requesters) }
@ -1448,10 +1449,9 @@ RSpec.describe Group, feature_category: :groups_and_projects do
let(:developer) { create(:user) }
it 'returns the owners of a Group' do
group.add_owner(owner)
group.add_developer(developer)
members = setup_group_members(group)
expect(group.owners).to eq([owner])
expect(group.owners).to eq([members[:owner]])
end
end

View File

@ -2090,7 +2090,7 @@ RSpec.describe User, feature_category: :user_profile do
user = create(:user)
expect(user.incoming_email_token).to eql('gitlab')
expect(user.incoming_email_token).to eql("glimt-gitlab")
end
end
@ -2137,6 +2137,12 @@ RSpec.describe User, feature_category: :user_profile do
expect(user.reload.feed_token).to eq feed_token
end
it 'returns feed tokens with a prefix' do
user = create(:user)
expect(user.feed_token).to start_with('glft-')
end
it 'ensures no feed token when disabled' do
allow(Gitlab::CurrentSettings).to receive(:disable_feed_token).and_return(true)
@ -2184,7 +2190,7 @@ RSpec.describe User, feature_category: :user_profile do
describe 'enabled_static_object_token' do
let_it_be(:static_object_token) { 'ilqx6jm1u945macft4eff0nw' }
it 'returns incoming email token when supported' do
it 'returns static object token when supported' do
allow(Gitlab::CurrentSettings).to receive(:static_objects_external_storage_enabled?).and_return(true)
user = create(:user, static_object_token: static_object_token)
@ -2212,6 +2218,14 @@ RSpec.describe User, feature_category: :user_profile do
expect(user.enabled_incoming_email_token).to eq(incoming_email_token)
end
it 'returns incoming mail tokens with a prefix' do
allow(Gitlab::Email::IncomingEmail).to receive(:supports_issue_creation?).and_return(true)
user = create(:user)
expect(user.enabled_incoming_email_token).to start_with('glimt-')
end
it 'returns `nil` when not supported' do
allow(Gitlab::Email::IncomingEmail).to receive(:supports_issue_creation?).and_return(false)

View File

@ -9,7 +9,24 @@ module TestEnv
ComponentFailedToInstallError = Class.new(StandardError)
# When developing the seed repository, comment out the branch you will modify.
# https://gitlab.com/gitlab-org/gitlab-test is used to seed your local gdk
# GitLab application and is also used in rspec tests. Because of this, when
# building and testing features that require a specific type of file, you can
# add them to the gitlab-test repo in order to access that blob during
# development or testing.
#
# To add new branches
#
# 1. Push a new branch to gitlab-org/gitlab-test.
# 2. Execute rm -rf tmp/tests in your gitlab repo.
# 3. Add your branch and its HEAD commit sha to the BRANCH_SHA hash
#
# To add new commits to an existing branch
#
# 1. Push a new commit to a branch in gitlab-org/gitlab-test.
# 2. Execute rm -rf tmp/tests in your gitlab repo.
# 3. Update the HEAD sha value in the BRANCH_SHA hash
#
BRANCH_SHA = {
'signed-commits' => 'c7794c1',
'gpg-signed' => '8a852d5',
@ -94,7 +111,8 @@ module TestEnv
'smime-signed-commits' => 'ed775cc',
'Ääh-test-utf-8' => '7975be0',
'ssh-signed-commit' => '7b5160f',
'changes-with-whitespace' => 'f2d141fadb33ceaafc95667c1a0a308ad5edc5f9'
'changes-with-whitespace' => 'f2d141fadb33ceaafc95667c1a0a308ad5edc5f9',
'lock-detection' => '1ada92f78a19f27cb442a0a205f1c451a3a15432'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily

View File

@ -8267,6 +8267,11 @@ layout-base@^2.0.0:
resolved "https://registry.yarnpkg.com/layout-base/-/layout-base-2.0.1.tgz#d0337913586c90f9c2c075292069f5c2da5dd285"
integrity sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==
leaflet@^1.9.4:
version "1.9.4"
resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.4.tgz#23fae724e282fa25745aff82ca4d394748db7d8d"
integrity sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==
leven@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"