Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ce493944f4
commit
60082b335c
|
@ -135,8 +135,11 @@ export default {
|
||||||
:label-for="fieldId"
|
:label-for="fieldId"
|
||||||
:invalid-feedback="__('This field is required.')"
|
:invalid-feedback="__('This field is required.')"
|
||||||
:state="valid"
|
:state="valid"
|
||||||
:description="help"
|
|
||||||
>
|
>
|
||||||
|
<template #description>
|
||||||
|
<span v-html="help"></span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-if="isCheckbox">
|
<template v-if="isCheckbox">
|
||||||
<input :name="fieldName" type="hidden" value="false" />
|
<input :name="fieldName" type="hidden" value="false" />
|
||||||
<gl-form-checkbox v-model="model" v-bind="sharedProps">
|
<gl-form-checkbox v-model="model" v-bind="sharedProps">
|
||||||
|
|
|
@ -54,9 +54,8 @@ module IntegrationsActions
|
||||||
end
|
end
|
||||||
|
|
||||||
def integration
|
def integration
|
||||||
# Using instance variable `@service` still required as it's used in ServiceParams
|
# Using instance variable `@service` still required as it's used in ServiceParams.
|
||||||
# and app/views/shared/_service_settings.html.haml. Should be removed once
|
# Should be removed once that is refactored to use `@integration`.
|
||||||
# those 2 are refactored to use `@integration`.
|
|
||||||
@integration = @service ||= find_or_initialize_integration(params[:id]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
@integration = @service ||= find_or_initialize_integration(params[:id]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
# min_access_level: integer
|
# min_access_level: integer
|
||||||
# last_activity_after: datetime
|
# last_activity_after: datetime
|
||||||
# last_activity_before: datetime
|
# last_activity_before: datetime
|
||||||
|
# repository_storage: string
|
||||||
#
|
#
|
||||||
class ProjectsFinder < UnionFinder
|
class ProjectsFinder < UnionFinder
|
||||||
include CustomAttributesFilter
|
include CustomAttributesFilter
|
||||||
|
@ -75,6 +76,7 @@ class ProjectsFinder < UnionFinder
|
||||||
collection = by_deleted_status(collection)
|
collection = by_deleted_status(collection)
|
||||||
collection = by_last_activity_after(collection)
|
collection = by_last_activity_after(collection)
|
||||||
collection = by_last_activity_before(collection)
|
collection = by_last_activity_before(collection)
|
||||||
|
collection = by_repository_storage(collection)
|
||||||
collection
|
collection
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -197,6 +199,14 @@ class ProjectsFinder < UnionFinder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def by_repository_storage(items)
|
||||||
|
if params[:repository_storage].present?
|
||||||
|
items.where(repository_storage: params[:repository_storage]) # rubocop: disable CodeReuse/ActiveRecord
|
||||||
|
else
|
||||||
|
items
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def sort(items)
|
def sort(items)
|
||||||
params[:sort].present? ? items.sort_by_attribute(params[:sort]) : items.projects_order_id_desc
|
params[:sort].present? ? items.sort_by_attribute(params[:sort]) : items.projects_order_id_desc
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,13 +44,6 @@ module ServicesHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def event_action_description(action)
|
|
||||||
case action
|
|
||||||
when "comment"
|
|
||||||
s_("ProjectService|Comment will be posted on each event")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def service_save_button
|
def service_save_button
|
||||||
button_tag(class: 'btn btn-success', type: 'submit', data: { qa_selector: 'save_changes_button' }) do
|
button_tag(class: 'btn btn-success', type: 'submit', data: { qa_selector: 'save_changes_button' }) do
|
||||||
icon('spinner spin', class: 'hidden js-btn-spinner') +
|
icon('spinner spin', class: 'hidden js-btn-spinner') +
|
||||||
|
@ -90,7 +83,7 @@ module ServicesHelper
|
||||||
|
|
||||||
def scoped_test_integration_path(integration)
|
def scoped_test_integration_path(integration)
|
||||||
if @project.present?
|
if @project.present?
|
||||||
test_project_settings_integration_path(@project, integration)
|
test_project_service_path(@project, integration)
|
||||||
elsif @group.present?
|
elsif @group.present?
|
||||||
test_group_settings_integration_path(@group, integration)
|
test_group_settings_integration_path(@group, integration)
|
||||||
else
|
else
|
||||||
|
@ -102,22 +95,36 @@ module ServicesHelper
|
||||||
Feature.enabled?(:integration_form_refactor, @project)
|
Feature.enabled?(:integration_form_refactor, @project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def trigger_events_for_service
|
def integration_form_data(integration)
|
||||||
return [] unless integration_form_refactor?
|
{
|
||||||
|
show_active: integration.show_active_box?.to_s,
|
||||||
ServiceEventSerializer.new(service: @service).represent(@service.configurable_events).to_json
|
activated: (integration.active || integration.new_record?).to_s,
|
||||||
|
type: integration.to_param,
|
||||||
|
merge_request_events: integration.merge_requests_events.to_s,
|
||||||
|
commit_events: integration.commit_events.to_s,
|
||||||
|
enable_comments: integration.comment_on_event_enabled.to_s,
|
||||||
|
comment_detail: integration.comment_detail,
|
||||||
|
trigger_events: trigger_events_for_service(integration),
|
||||||
|
fields: fields_for_service(integration)
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def fields_for_service
|
def trigger_events_for_service(integration)
|
||||||
return [] unless integration_form_refactor?
|
return [] unless integration_form_refactor?
|
||||||
|
|
||||||
ServiceFieldSerializer.new(service: @service).represent(@service.global_fields).to_json
|
ServiceEventSerializer.new(service: integration).represent(integration.configurable_events).to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_service_trigger_events?
|
def fields_for_service(integration)
|
||||||
return false if @service.is_a?(JiraService) || integration_form_refactor?
|
return [] unless integration_form_refactor?
|
||||||
|
|
||||||
@service.configurable_events.present?
|
ServiceFieldSerializer.new(service: integration).represent(integration.global_fields).to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_service_trigger_events?(integration)
|
||||||
|
return false if integration.is_a?(JiraService) || integration_form_refactor?
|
||||||
|
|
||||||
|
integration.configurable_events.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
extend self
|
extend self
|
||||||
|
|
|
@ -1925,6 +1925,7 @@ class Project < ApplicationRecord
|
||||||
.append(key: 'CI_PROJECT_PATH', value: full_path)
|
.append(key: 'CI_PROJECT_PATH', value: full_path)
|
||||||
.append(key: 'CI_PROJECT_PATH_SLUG', value: full_path_slug)
|
.append(key: 'CI_PROJECT_PATH_SLUG', value: full_path_slug)
|
||||||
.append(key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path)
|
.append(key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path)
|
||||||
|
.append(key: 'CI_PROJECT_ROOT_NAMESPACE', value: namespace.root_ancestor.path)
|
||||||
.append(key: 'CI_PROJECT_URL', value: web_url)
|
.append(key: 'CI_PROJECT_URL', value: web_url)
|
||||||
.append(key: 'CI_PROJECT_VISIBILITY', value: Gitlab::VisibilityLevel.string_level(visibility_level))
|
.append(key: 'CI_PROJECT_VISIBILITY', value: Gitlab::VisibilityLevel.string_level(visibility_level))
|
||||||
.append(key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: repository_languages.map(&:name).join(',').downcase)
|
.append(key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: repository_languages.map(&:name).join(',').downcase)
|
||||||
|
|
|
@ -105,6 +105,9 @@ class GlobalPolicy < BasePolicy
|
||||||
enable :update_custom_attribute
|
enable :update_custom_attribute
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# We can't use `read_statistics` because the user may have different permissions for different projects
|
||||||
|
rule { admin }.enable :use_project_statistics_filters
|
||||||
|
|
||||||
rule { external_user }.prevent :create_snippet
|
rule { external_user }.prevent :create_snippet
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ module Metrics
|
||||||
|
|
||||||
def fetch_dashboard
|
def fetch_dashboard
|
||||||
uid = GrafanaUidParser.new(grafana_url, project).parse
|
uid = GrafanaUidParser.new(grafana_url, project).parse
|
||||||
raise DashboardProcessingError.new('Dashboard uid not found') unless uid
|
raise DashboardProcessingError.new(_('Dashboard uid not found')) unless uid
|
||||||
|
|
||||||
response = client.get_dashboard(uid: uid)
|
response = client.get_dashboard(uid: uid)
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ module Metrics
|
||||||
|
|
||||||
def fetch_datasource(dashboard)
|
def fetch_datasource(dashboard)
|
||||||
name = DatasourceNameParser.new(grafana_url, dashboard).parse
|
name = DatasourceNameParser.new(grafana_url, dashboard).parse
|
||||||
raise DashboardProcessingError.new('Datasource name not found') unless name
|
raise DashboardProcessingError.new(_('Datasource name not found')) unless name
|
||||||
|
|
||||||
response = client.get_datasource(name: name)
|
response = client.get_datasource(name: name)
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ module Metrics
|
||||||
def parse_json(json)
|
def parse_json(json)
|
||||||
Gitlab::Json.parse(json, symbolize_names: true)
|
Gitlab::Json.parse(json, symbolize_names: true)
|
||||||
rescue JSON::ParserError
|
rescue JSON::ParserError
|
||||||
raise DashboardProcessingError.new('Grafana response contains invalid json')
|
raise DashboardProcessingError.new(_('Grafana response contains invalid json'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ module Metrics
|
||||||
end
|
end
|
||||||
|
|
||||||
def invalid_embed_json!(message)
|
def invalid_embed_json!(message)
|
||||||
raise DashboardProcessingError.new("Parsing error for param :embed_json. #{message}")
|
raise DashboardProcessingError.new(_("Parsing error for param :embed_json. %{message}") % { message: message })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
%p #{@service.description} template.
|
%p #{@service.description} template.
|
||||||
|
|
||||||
= form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'fieldset-form' } do |form|
|
= form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'fieldset-form' } do |form|
|
||||||
= render 'shared/service_settings', form: form, service: @service
|
= render 'shared/service_settings', form: form, integration: @service
|
||||||
|
|
||||||
.footer-block.row-content-block
|
.footer-block.row-content-block
|
||||||
= form.submit 'Save', class: 'btn btn-success'
|
= form.submit 'Save', class: 'btn btn-success'
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
- add_to_breadcrumbs "Service Templates", admin_application_settings_services_path
|
- add_to_breadcrumbs "Service Templates", admin_application_settings_services_path
|
||||||
- breadcrumb_title @service.title
|
- breadcrumb_title @service.title
|
||||||
- page_title @service.title, "Service Templates"
|
- page_title @service.title, "Service Templates"
|
||||||
|
- @content_class = 'limit-container-width' unless fluid_layout
|
||||||
|
|
||||||
= render 'form'
|
= render 'form'
|
||||||
|
|
|
@ -216,7 +216,7 @@
|
||||||
%strong.fly-out-top-item-name
|
%strong.fly-out-top-item-name
|
||||||
= _('Appearance')
|
= _('Appearance')
|
||||||
|
|
||||||
= nav_link(controller: :application_settings) do
|
= nav_link(controller: [:application_settings, :integrations]) do
|
||||||
= link_to general_admin_application_settings_path do
|
= link_to general_admin_application_settings_path do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= sprite_icon('settings')
|
= sprite_icon('settings')
|
||||||
|
@ -224,7 +224,7 @@
|
||||||
= _('Settings')
|
= _('Settings')
|
||||||
|
|
||||||
%ul.sidebar-sub-level-items.qa-admin-sidebar-settings-submenu
|
%ul.sidebar-sub-level-items.qa-admin-sidebar-settings-submenu
|
||||||
= nav_link(controller: :application_settings, html_options: { class: "fly-out-top-item" } ) do
|
= nav_link(controller: [:application_settings, :integrations], html_options: { class: "fly-out-top-item" } ) do
|
||||||
= link_to general_admin_application_settings_path do
|
= link_to general_admin_application_settings_path do
|
||||||
%strong.fly-out-top-item-name
|
%strong.fly-out-top-item-name
|
||||||
= _('Settings')
|
= _('Settings')
|
||||||
|
@ -233,7 +233,7 @@
|
||||||
= link_to general_admin_application_settings_path, title: _('General'), class: 'qa-admin-settings-general-item' do
|
= link_to general_admin_application_settings_path, title: _('General'), class: 'qa-admin-settings-general-item' do
|
||||||
%span
|
%span
|
||||||
= _('General')
|
= _('General')
|
||||||
= nav_link(path: 'application_settings#integrations') do
|
= nav_link(path: ['application_settings#integrations', 'integrations#edit']) do
|
||||||
= link_to integrations_admin_application_settings_path, title: _('Integrations'), data: { qa_selector: 'integration_settings_link' } do
|
= link_to integrations_admin_application_settings_path, title: _('Integrations'), data: { qa_selector: 'integration_settings_link' } do
|
||||||
%span
|
%span
|
||||||
= _('Integrations')
|
= _('Integrations')
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
%p= @service.detailed_description
|
%p= @service.detailed_description
|
||||||
.col-lg-8
|
.col-lg-8
|
||||||
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
|
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
|
||||||
= render 'shared/service_settings', form: form, service: @service
|
= render 'shared/service_settings', form: form, integration: @service
|
||||||
.footer-block.row-content-block
|
.footer-block.row-content-block
|
||||||
%input{ id: 'services_redirect_to', type: 'hidden', name: 'redirect_to', value: request.referrer }
|
%input{ id: 'services_redirect_to', type: 'hidden', name: 'redirect_to', value: request.referrer }
|
||||||
= service_save_button
|
= service_save_button
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
= form_errors(@service)
|
= form_errors(integration)
|
||||||
|
|
||||||
- if lookup_context.template_exists?('help', "projects/services/#{@service.to_param}", true)
|
- if lookup_context.template_exists?('help', "projects/services/#{integration.to_param}", true)
|
||||||
= render "projects/services/#{@service.to_param}/help", subject: @service
|
= render "projects/services/#{integration.to_param}/help", subject: integration
|
||||||
- elsif @service.help.present?
|
- elsif integration.help.present?
|
||||||
.info-well
|
.info-well
|
||||||
.well-segment
|
.well-segment
|
||||||
= markdown @service.help
|
= markdown integration.help
|
||||||
|
|
||||||
.service-settings
|
.service-settings
|
||||||
.js-vue-integration-settings{ data: { show_active: @service.show_active_box?.to_s, activated: (@service.active || @service.new_record?).to_s, type: @service.to_param, merge_request_events: @service.merge_requests_events.to_s,
|
.js-vue-integration-settings{ data: integration_form_data(integration) }
|
||||||
commit_events: @service.commit_events.to_s, enable_comments: @service.comment_on_event_enabled.to_s, comment_detail: @service.comment_detail, trigger_events: trigger_events_for_service, fields: fields_for_service } }
|
|
||||||
|
|
||||||
- if show_service_trigger_events?
|
- if show_service_trigger_events?(integration)
|
||||||
.form-group.row
|
.form-group.row
|
||||||
%label.col-form-label.col-sm-2= _('Trigger')
|
%label.col-form-label.col-sm-2= _('Trigger')
|
||||||
|
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
- @service.configurable_events.each do |event|
|
- integration.configurable_events.each do |event|
|
||||||
.form-group
|
.form-group
|
||||||
.form-check
|
.form-check
|
||||||
= form.check_box service_event_field_name(event), class: 'form-check-input'
|
= form.check_box service_event_field_name(event), class: 'form-check-input'
|
||||||
|
@ -24,14 +23,14 @@ commit_events: @service.commit_events.to_s, enable_comments: @service.comment_on
|
||||||
%strong
|
%strong
|
||||||
= event.humanize
|
= event.humanize
|
||||||
|
|
||||||
- field = @service.event_field(event)
|
- field = integration.event_field(event)
|
||||||
|
|
||||||
- if field
|
- if field
|
||||||
= form.text_field field[:name], class: "form-control", placeholder: field[:placeholder]
|
= form.text_field field[:name], class: "form-control", placeholder: field[:placeholder]
|
||||||
|
|
||||||
%p.text-muted
|
%p.text-muted
|
||||||
= @service.class.event_description(event)
|
= integration.class.event_description(event)
|
||||||
|
|
||||||
- unless integration_form_refactor?
|
- unless integration_form_refactor?
|
||||||
- @service.global_fields.each do |field|
|
- integration.global_fields.each do |field|
|
||||||
= render 'shared/field', form: form, field: field
|
= render 'shared/field', form: form, field: field
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
- add_to_breadcrumbs _('Integrations'), scoped_integrations_path
|
- add_to_breadcrumbs _('Integrations'), scoped_integrations_path
|
||||||
- breadcrumb_title @integration.title
|
- breadcrumb_title @integration.title
|
||||||
- page_title @integration.title, _('Integrations')
|
- page_title @integration.title, _('Integrations')
|
||||||
|
- @content_class = 'limit-container-width' unless fluid_layout
|
||||||
|
|
||||||
= render 'shared/integrations/form', integration: @integration
|
= render 'shared/integrations/form', integration: @integration
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Refine UI of integration form
|
||||||
|
merge_request: 34707
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add CI_PROJECT_ROOT_NAMESPACE predefined environment variable
|
||||||
|
merge_request: 34733
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Allow advanced API projects filtering for admins
|
||||||
|
merge_request: 32879
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix Gitaly duration timings of BlobService RPCs
|
||||||
|
merge_request: 34906
|
||||||
|
author:
|
||||||
|
type: other
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddIndexOnRepositorySizeAndProjectIdToProjectStatistics < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_concurrent_index :project_statistics, [:repository_size, :project_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_concurrent_index :project_statistics, [:repository_size, :project_id]
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddIndexOnStorageSizeAndProjectIdToProjectStatistics < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_concurrent_index :project_statistics, [:storage_size, :project_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_concurrent_index :project_statistics, [:storage_size, :project_id]
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddIndexOnWikiSizeAndProjectIdToProjectStatistics < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_concurrent_index :project_statistics, [:wiki_size, :project_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_concurrent_index :project_statistics, [:wiki_size, :project_id]
|
||||||
|
end
|
||||||
|
end
|
|
@ -10643,6 +10643,12 @@ CREATE INDEX index_project_statistics_on_namespace_id ON public.project_statisti
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_project_statistics_on_project_id ON public.project_statistics USING btree (project_id);
|
CREATE UNIQUE INDEX index_project_statistics_on_project_id ON public.project_statistics USING btree (project_id);
|
||||||
|
|
||||||
|
CREATE INDEX index_project_statistics_on_repository_size_and_project_id ON public.project_statistics USING btree (repository_size, project_id);
|
||||||
|
|
||||||
|
CREATE INDEX index_project_statistics_on_storage_size_and_project_id ON public.project_statistics USING btree (storage_size, project_id);
|
||||||
|
|
||||||
|
CREATE INDEX index_project_statistics_on_wiki_size_and_project_id ON public.project_statistics USING btree (wiki_size, project_id);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_project_tracing_settings_on_project_id ON public.project_tracing_settings USING btree (project_id);
|
CREATE UNIQUE INDEX index_project_tracing_settings_on_project_id ON public.project_tracing_settings USING btree (project_id);
|
||||||
|
|
||||||
CREATE INDEX index_projects_api_created_at_id_desc ON public.projects USING btree (created_at, id DESC);
|
CREATE INDEX index_projects_api_created_at_id_desc ON public.projects USING btree (created_at, id DESC);
|
||||||
|
@ -14055,6 +14061,9 @@ COPY "schema_migrations" (version) FROM STDIN;
|
||||||
20200604174558
|
20200604174558
|
||||||
20200605003204
|
20200605003204
|
||||||
20200605093113
|
20200605093113
|
||||||
|
20200605160806
|
||||||
|
20200605160836
|
||||||
|
20200605160851
|
||||||
20200608072931
|
20200608072931
|
||||||
20200608075553
|
20200608075553
|
||||||
20200608214008
|
20200608214008
|
||||||
|
|
|
@ -103,10 +103,10 @@ Some feature flags can be enabled or disabled on a per project basis:
|
||||||
Feature.enable(:<feature flag>, Project.find(<project id>))
|
Feature.enable(:<feature flag>, Project.find(<project id>))
|
||||||
```
|
```
|
||||||
|
|
||||||
For example, to enable the [`:release_evidence_collection`](../ci/junit_test_reports.md#enabling-the-feature) feature flag for project `1234`:
|
For example, to enable the [`:junit_pipeline_view`](../ci/junit_test_reports.md#enabling-the-junit-test-reports-feature-core-only) feature flag for project `1234`:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Feature.enable(:release_evidence_collection, Project.find(1234))
|
Feature.enable(:junit_pipeline_view, Project.find(1234))
|
||||||
```
|
```
|
||||||
|
|
||||||
When the feature is ready, GitLab will remove the feature flag, the option for
|
When the feature is ready, GitLab will remove the feature flag, the option for
|
||||||
|
|
|
@ -142,7 +142,7 @@ Example of response
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202525) in GitLab 13.0.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202525) in GitLab 13.0.
|
||||||
|
|
||||||
CAUTION: **Caution:**
|
CAUTION: **Caution:**
|
||||||
This API route is part of the [JUnit test report](../ci/junit_test_reports.md) feature. It is protected by a [feature flag](../development/feature_flags/index.md) that is **disabled** due to performance issues with very large data sets. See [the documentation for the feature](../ci/junit_test_reports.md#enabling-the-feature) for further details.
|
This API route is part of the [JUnit test report](../ci/junit_test_reports.md) feature. It is protected by a [feature flag](../development/feature_flags/index.md) that is **disabled** due to performance issues with very large data sets.
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
GET /projects/:id/pipelines/:pipeline_id/test_report
|
GET /projects/:id/pipelines/:pipeline_id/test_report
|
||||||
|
|
|
@ -45,7 +45,7 @@ GET /projects
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `archived` | boolean | no | Limit by archived status |
|
| `archived` | boolean | no | Limit by archived status |
|
||||||
| `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` |
|
| `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` |
|
||||||
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
|
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. `repository_size`, `storage_size`, or `wiki_size` fields are only allowed for admins. Default is `created_at` |
|
||||||
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
|
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
|
||||||
| `search` | string | no | Return list of projects matching the search criteria |
|
| `search` | string | no | Return list of projects matching the search criteria |
|
||||||
| `search_namespaces` | boolean | no | Include ancestor namespaces when matching search criteria. Default is `false` |
|
| `search_namespaces` | boolean | no | Include ancestor namespaces when matching search criteria. Default is `false` |
|
||||||
|
@ -65,6 +65,7 @@ GET /projects
|
||||||
| `id_before` | integer | no | Limit results to projects with IDs less than the specified ID |
|
| `id_before` | integer | no | Limit results to projects with IDs less than the specified ID |
|
||||||
| `last_activity_after` | datetime | no | Limit results to projects with last_activity after specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ |
|
| `last_activity_after` | datetime | no | Limit results to projects with last_activity after specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ |
|
||||||
| `last_activity_before` | datetime | no | Limit results to projects with last_activity before specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ |
|
| `last_activity_before` | datetime | no | Limit results to projects with last_activity before specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ |
|
||||||
|
| `repository_storage` | string | no | Limit results to projects stored on repository_storage. Available for admins only. |
|
||||||
|
|
||||||
NOTE: **Note:**
|
NOTE: **Note:**
|
||||||
This endpoint supports [keyset pagination](README.md#keyset-based-pagination) for selected `order_by` options.
|
This endpoint supports [keyset pagination](README.md#keyset-based-pagination) for selected `order_by` options.
|
||||||
|
|
|
@ -206,7 +206,7 @@ cunit:
|
||||||
junit: ./my-cunit-test.xml
|
junit: ./my-cunit-test.xml
|
||||||
```
|
```
|
||||||
|
|
||||||
### .Net example
|
### .NET example
|
||||||
|
|
||||||
The [JunitXML.TestLogger](https://www.nuget.org/packages/JunitXml.TestLogger/) NuGet
|
The [JunitXML.TestLogger](https://www.nuget.org/packages/JunitXml.TestLogger/) NuGet
|
||||||
package can generate test reports for .Net Framework and .Net Core applications. The following
|
package can generate test reports for .Net Framework and .Net Core applications. The following
|
||||||
|
@ -234,7 +234,9 @@ Test:
|
||||||
|
|
||||||
## Viewing JUnit test reports on GitLab
|
## Viewing JUnit test reports on GitLab
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24792) in GitLab 12.5.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24792) in GitLab 12.5.
|
||||||
|
> - It's deployed behind a feature flag, disabled by default.
|
||||||
|
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enabling-the-junit-test-reports-feature-core-only). **(CORE ONLY)**
|
||||||
|
|
||||||
If JUnit XML files are generated and uploaded as part of a pipeline, these reports
|
If JUnit XML files are generated and uploaded as part of a pipeline, these reports
|
||||||
can be viewed inside the pipelines details page. The **Tests** tab on this page will
|
can be viewed inside the pipelines details page. The **Tests** tab on this page will
|
||||||
|
@ -248,7 +250,7 @@ with failed showing at the top, skipped next and successful cases last.
|
||||||
|
|
||||||
You can also retrieve the reports via the [GitLab API](../api/pipelines.md#get-a-pipelines-test-report).
|
You can also retrieve the reports via the [GitLab API](../api/pipelines.md#get-a-pipelines-test-report).
|
||||||
|
|
||||||
### Enabling the feature
|
### Enabling the JUnit test reports feature **(CORE ONLY)**
|
||||||
|
|
||||||
This feature comes with the `:junit_pipeline_view` feature flag disabled by default. This
|
This feature comes with the `:junit_pipeline_view` feature flag disabled by default. This
|
||||||
feature is disabled due to some performance issues with very large data sets.
|
feature is disabled due to some performance issues with very large data sets.
|
||||||
|
@ -266,7 +268,9 @@ Feature.enable(:junit_pipeline_view, Project.find(<your-project-id-here>))
|
||||||
|
|
||||||
## Viewing JUnit screenshots on GitLab
|
## Viewing JUnit screenshots on GitLab
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202114) in GitLab 13.0.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202114) in GitLab 13.0.
|
||||||
|
> - It's deployed behind a feature flag, disabled by default.
|
||||||
|
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enabling-the-junit-screenshots-feature-core-only). **(CORE ONLY)**
|
||||||
|
|
||||||
If JUnit XML files contain an `attachment` tag, GitLab parses the attachment.
|
If JUnit XML files contain an `attachment` tag, GitLab parses the attachment.
|
||||||
|
|
||||||
|
@ -280,7 +284,7 @@ Upload your screenshots as [artifacts](pipelines/job_artifacts.md#artifactsrepor
|
||||||
|
|
||||||
When [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/6061) is complete, the attached file will be visible on the pipeline details page.
|
When [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/6061) is complete, the attached file will be visible on the pipeline details page.
|
||||||
|
|
||||||
### Enabling the feature
|
### Enabling the JUnit screenshots feature **(CORE ONLY)**
|
||||||
|
|
||||||
This feature comes with the `:junit_pipeline_screenshots_view` feature flag disabled by default.
|
This feature comes with the `:junit_pipeline_screenshots_view` feature flag disabled by default.
|
||||||
|
|
||||||
|
|
|
@ -341,6 +341,7 @@ export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-foss"
|
||||||
export CI_PROJECT_NAME="gitlab-foss"
|
export CI_PROJECT_NAME="gitlab-foss"
|
||||||
export CI_PROJECT_TITLE="GitLab FOSS"
|
export CI_PROJECT_TITLE="GitLab FOSS"
|
||||||
export CI_PROJECT_NAMESPACE="gitlab-org"
|
export CI_PROJECT_NAMESPACE="gitlab-org"
|
||||||
|
export CI_PROJECT_ROOT_NAMESPACE="gitlab-org"
|
||||||
export CI_PROJECT_PATH="gitlab-org/gitlab-foss"
|
export CI_PROJECT_PATH="gitlab-org/gitlab-foss"
|
||||||
export CI_PROJECT_URL="https://example.com/gitlab-org/gitlab-foss"
|
export CI_PROJECT_URL="https://example.com/gitlab-org/gitlab-foss"
|
||||||
export CI_REGISTRY="registry.example.com"
|
export CI_REGISTRY="registry.example.com"
|
||||||
|
@ -909,6 +910,8 @@ if [[ -d "/builds/gitlab-examples/ci-debug-trace/.git" ]]; then
|
||||||
++ CI_PROJECT_PATH_SLUG=gitlab-examples-ci-debug-trace
|
++ CI_PROJECT_PATH_SLUG=gitlab-examples-ci-debug-trace
|
||||||
++ export CI_PROJECT_NAMESPACE=gitlab-examples
|
++ export CI_PROJECT_NAMESPACE=gitlab-examples
|
||||||
++ CI_PROJECT_NAMESPACE=gitlab-examples
|
++ CI_PROJECT_NAMESPACE=gitlab-examples
|
||||||
|
++ export CI_PROJECT_ROOT_NAMESPACE=gitlab-examples
|
||||||
|
++ CI_PROJECT_ROOT_NAMESPACE=gitlab-examples
|
||||||
++ export CI_PROJECT_URL=https://gitlab.com/gitlab-examples/ci-debug-trace
|
++ export CI_PROJECT_URL=https://gitlab.com/gitlab-examples/ci-debug-trace
|
||||||
++ CI_PROJECT_URL=https://gitlab.com/gitlab-examples/ci-debug-trace
|
++ CI_PROJECT_URL=https://gitlab.com/gitlab-examples/ci-debug-trace
|
||||||
++ export CI_PROJECT_VISIBILITY=public
|
++ export CI_PROJECT_VISIBILITY=public
|
||||||
|
|
|
@ -99,6 +99,7 @@ You can add a command to your `.gitlab-ci.yml` file to
|
||||||
| `CI_PROJECT_ID` | all | all | The unique ID of the current project that GitLab CI/CD uses internally |
|
| `CI_PROJECT_ID` | all | all | The unique ID of the current project that GitLab CI/CD uses internally |
|
||||||
| `CI_PROJECT_NAME` | 8.10 | 0.5 | The name of the directory for the project that is currently being built. For example, if the project URL is `gitlab.example.com/group-name/project-1`, the `CI_PROJECT_NAME` would be `project-1`. |
|
| `CI_PROJECT_NAME` | 8.10 | 0.5 | The name of the directory for the project that is currently being built. For example, if the project URL is `gitlab.example.com/group-name/project-1`, the `CI_PROJECT_NAME` would be `project-1`. |
|
||||||
| `CI_PROJECT_NAMESPACE` | 8.10 | 0.5 | The project namespace (username or group name) that is currently being built |
|
| `CI_PROJECT_NAMESPACE` | 8.10 | 0.5 | The project namespace (username or group name) that is currently being built |
|
||||||
|
| `CI_PROJECT_ROOT_NAMESPACE` | 13.2 | 0.5 | The **root** project namespace (username or group name) that is currently being built. For example, if `CI_PROJECT_NAME` is `root-group/child-group/grandchild-group`, `CI_PROJECT_ROOT_NAMESPACE` would be `root-group`. |
|
||||||
| `CI_PROJECT_PATH` | 8.10 | 0.5 | The namespace with project name |
|
| `CI_PROJECT_PATH` | 8.10 | 0.5 | The namespace with project name |
|
||||||
| `CI_PROJECT_PATH_SLUG` | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
|
| `CI_PROJECT_PATH_SLUG` | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
|
||||||
| `CI_PROJECT_REPOSITORY_LANGUAGES` | 12.3 | all | Comma-separated, lowercased list of the languages used in the repository (e.g. `ruby,javascript,html,css`) |
|
| `CI_PROJECT_REPOSITORY_LANGUAGES` | 12.3 | all | Comma-separated, lowercased list of the languages used in the repository (e.g. `ruby,javascript,html,css`) |
|
||||||
|
|
|
@ -543,6 +543,7 @@ module API
|
||||||
finder_params[:id_before] = params[:id_before] if params[:id_before]
|
finder_params[:id_before] = params[:id_before] if params[:id_before]
|
||||||
finder_params[:last_activity_after] = params[:last_activity_after] if params[:last_activity_after]
|
finder_params[:last_activity_after] = params[:last_activity_after] if params[:last_activity_after]
|
||||||
finder_params[:last_activity_before] = params[:last_activity_before] if params[:last_activity_before]
|
finder_params[:last_activity_before] = params[:last_activity_before] if params[:last_activity_before]
|
||||||
|
finder_params[:repository_storage] = params[:repository_storage] if params[:repository_storage]
|
||||||
finder_params
|
finder_params
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ module API
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
extend Grape::API::Helpers
|
extend Grape::API::Helpers
|
||||||
|
|
||||||
|
STATISTICS_SORT_PARAMS = %w[storage_size repository_size wiki_size].freeze
|
||||||
|
|
||||||
params :optional_project_params_ce do
|
params :optional_project_params_ce do
|
||||||
optional :description, type: String, desc: 'The description of the project'
|
optional :description, type: String, desc: 'The description of the project'
|
||||||
optional :build_git_strategy, type: String, values: %w(fetch clone), desc: 'The Git strategy. Defaults to `fetch`'
|
optional :build_git_strategy, type: String, values: %w(fetch clone), desc: 'The Git strategy. Defaults to `fetch`'
|
||||||
|
|
|
@ -17,6 +17,7 @@ module API
|
||||||
projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled]
|
projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled]
|
||||||
projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
|
projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
|
||||||
projects = projects.with_statistics if params[:statistics]
|
projects = projects.with_statistics if params[:statistics]
|
||||||
|
projects = projects.joins(:statistics) if params[:order_by].include?('project_statistics') # rubocop: disable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
lang = params[:with_programming_language]
|
lang = params[:with_programming_language]
|
||||||
projects = projects.with_programming_language(lang) if lang
|
projects = projects.with_programming_language(lang) if lang
|
||||||
|
@ -28,6 +29,20 @@ module API
|
||||||
attrs.delete(:repository_storage) unless can?(current_user, :change_repository_storage, project)
|
attrs.delete(:repository_storage) unless can?(current_user, :change_repository_storage, project)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def verify_project_filters!(attrs)
|
||||||
|
attrs.delete(:repository_storage) unless can?(current_user, :use_project_statistics_filters)
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_statistics_order_by_projects!
|
||||||
|
return unless Helpers::ProjectsHelpers::STATISTICS_SORT_PARAMS.include?(params[:order_by])
|
||||||
|
|
||||||
|
params[:order_by] = if can?(current_user, :use_project_statistics_filters)
|
||||||
|
"project_statistics.#{params[:order_by]}"
|
||||||
|
else
|
||||||
|
route.params['order_by'][:default]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def delete_project(user_project)
|
def delete_project(user_project)
|
||||||
destroy_conditionally!(user_project) do
|
destroy_conditionally!(user_project) do
|
||||||
::Projects::DestroyService.new(user_project, current_user, {}).async_execute
|
::Projects::DestroyService.new(user_project, current_user, {}).async_execute
|
||||||
|
@ -52,8 +67,9 @@ module API
|
||||||
end
|
end
|
||||||
|
|
||||||
params :sort_params do
|
params :sort_params do
|
||||||
optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at],
|
optional :order_by, type: String,
|
||||||
default: 'created_at', desc: 'Return projects ordered by field'
|
values: %w[id name path created_at updated_at last_activity_at] + Helpers::ProjectsHelpers::STATISTICS_SORT_PARAMS,
|
||||||
|
default: 'created_at', desc: "Return projects ordered by field. #{Helpers::ProjectsHelpers::STATISTICS_SORT_PARAMS.join(', ')} are only available to admins."
|
||||||
optional :sort, type: String, values: %w[asc desc], default: 'desc',
|
optional :sort, type: String, values: %w[asc desc], default: 'desc',
|
||||||
desc: 'Return projects sorted in ascending and descending order'
|
desc: 'Return projects sorted in ascending and descending order'
|
||||||
end
|
end
|
||||||
|
@ -75,6 +91,7 @@ module API
|
||||||
optional :id_before, type: Integer, desc: 'Limit results to projects with IDs less than the specified ID'
|
optional :id_before, type: Integer, desc: 'Limit results to projects with IDs less than the specified ID'
|
||||||
optional :last_activity_after, type: DateTime, desc: 'Limit results to projects with last_activity after specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
|
optional :last_activity_after, type: DateTime, desc: 'Limit results to projects with last_activity after specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
|
||||||
optional :last_activity_before, type: DateTime, desc: 'Limit results to projects with last_activity before specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
|
optional :last_activity_before, type: DateTime, desc: 'Limit results to projects with last_activity before specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
|
||||||
|
optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins'
|
||||||
|
|
||||||
use :optional_filter_params_ee
|
use :optional_filter_params_ee
|
||||||
end
|
end
|
||||||
|
@ -88,10 +105,15 @@ module API
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_projects
|
def load_projects
|
||||||
ProjectsFinder.new(current_user: current_user, params: project_finder_params).execute
|
params = project_finder_params
|
||||||
|
verify_project_filters!(params)
|
||||||
|
|
||||||
|
ProjectsFinder.new(current_user: current_user, params: params).execute
|
||||||
end
|
end
|
||||||
|
|
||||||
def present_projects(projects, options = {})
|
def present_projects(projects, options = {})
|
||||||
|
verify_statistics_order_by_projects!
|
||||||
|
|
||||||
projects = reorder_projects(projects)
|
projects = reorder_projects(projects)
|
||||||
projects = apply_filters(projects)
|
projects = apply_filters(projects)
|
||||||
|
|
||||||
|
|
|
@ -60,12 +60,20 @@ module Gitlab
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def diff_file_with_old_path(old_path)
|
def diff_file_with_old_path(old_path, a_mode = nil)
|
||||||
diff_files.find { |diff_file| diff_file.old_path == old_path }
|
if Feature.enabled?(:file_identifier_hash) && a_mode.present?
|
||||||
|
diff_files.find { |diff_file| diff_file.old_path == old_path && diff_file.a_mode == a_mode }
|
||||||
|
else
|
||||||
|
diff_files.find { |diff_file| diff_file.old_path == old_path }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def diff_file_with_new_path(new_path)
|
def diff_file_with_new_path(new_path, b_mode = nil)
|
||||||
diff_files.find { |diff_file| diff_file.new_path == new_path }
|
if Feature.enabled?(:file_identifier_hash) && b_mode.present?
|
||||||
|
diff_files.find { |diff_file| diff_file.new_path == new_path && diff_file.b_mode == b_mode }
|
||||||
|
else
|
||||||
|
diff_files.find { |diff_file| diff_file.new_path == new_path }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_cache
|
def clear_cache
|
||||||
|
|
|
@ -42,6 +42,10 @@ module Gitlab
|
||||||
@cd_diffs ||= compare(new_diff_refs.start_sha, new_diff_refs.head_sha)
|
@cd_diffs ||= compare(new_diff_refs.start_sha, new_diff_refs.head_sha)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def diff_file(position)
|
||||||
|
position.diff_file(project.repository)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def compare(start_sha, head_sha, straight: false)
|
def compare(start_sha, head_sha, straight: false)
|
||||||
|
|
|
@ -8,6 +8,7 @@ module Gitlab
|
||||||
|
|
||||||
delegate \
|
delegate \
|
||||||
:project,
|
:project,
|
||||||
|
:diff_file,
|
||||||
:ac_diffs,
|
:ac_diffs,
|
||||||
:bd_diffs,
|
:bd_diffs,
|
||||||
:cd_diffs,
|
:cd_diffs,
|
||||||
|
|
|
@ -5,22 +5,26 @@ module Gitlab
|
||||||
class PositionTracer
|
class PositionTracer
|
||||||
class ImageStrategy < BaseStrategy
|
class ImageStrategy < BaseStrategy
|
||||||
def trace(position)
|
def trace(position)
|
||||||
|
a_path = position.old_path
|
||||||
b_path = position.new_path
|
b_path = position.new_path
|
||||||
|
diff_file = diff_file(position)
|
||||||
|
a_mode = diff_file&.a_mode
|
||||||
|
b_mode = diff_file&.b_mode
|
||||||
|
|
||||||
# If file exists in B->D (e.g. updated, renamed, removed), let the
|
# If file exists in B->D (e.g. updated, renamed, removed), let the
|
||||||
# note become outdated.
|
# note become outdated.
|
||||||
bd_diff = bd_diffs.diff_file_with_old_path(b_path)
|
bd_diff = bd_diffs.diff_file_with_old_path(b_path, b_mode)
|
||||||
|
|
||||||
return { position: new_position(position, bd_diff), outdated: true } if bd_diff
|
return { position: new_position(position, bd_diff), outdated: true } if bd_diff
|
||||||
|
|
||||||
# If file still exists in the new diff, update the position.
|
# If file still exists in the new diff, update the position.
|
||||||
cd_diff = cd_diffs.diff_file_with_new_path(bd_diff&.new_path || b_path)
|
cd_diff = cd_diffs.diff_file_with_new_path(b_path, b_mode)
|
||||||
|
|
||||||
return { position: new_position(position, cd_diff), outdated: false } if cd_diff
|
return { position: new_position(position, cd_diff), outdated: false } if cd_diff
|
||||||
|
|
||||||
# If file exists in A->C (e.g. rebased and same changes were present
|
# If file exists in A->C (e.g. rebased and same changes were present
|
||||||
# in target branch), let the note become outdated.
|
# in target branch), let the note become outdated.
|
||||||
ac_diff = ac_diffs.diff_file_with_old_path(position.old_path)
|
ac_diff = ac_diffs.diff_file_with_old_path(a_path, a_mode)
|
||||||
|
|
||||||
return { position: new_position(position, ac_diff), outdated: true } if ac_diff
|
return { position: new_position(position, ac_diff), outdated: true } if ac_diff
|
||||||
|
|
||||||
|
|
|
@ -76,16 +76,20 @@ module Gitlab
|
||||||
def trace_added_line(position)
|
def trace_added_line(position)
|
||||||
b_path = position.new_path
|
b_path = position.new_path
|
||||||
b_line = position.new_line
|
b_line = position.new_line
|
||||||
|
diff_file = diff_file(position)
|
||||||
|
b_mode = diff_file&.b_mode
|
||||||
|
|
||||||
bd_diff = bd_diffs.diff_file_with_old_path(b_path)
|
bd_diff = bd_diffs.diff_file_with_old_path(b_path, b_mode)
|
||||||
|
|
||||||
d_path = bd_diff&.new_path || b_path
|
d_path = bd_diff&.new_path || b_path
|
||||||
|
d_mode = bd_diff&.b_mode || b_mode
|
||||||
d_line = LineMapper.new(bd_diff).old_to_new(b_line)
|
d_line = LineMapper.new(bd_diff).old_to_new(b_line)
|
||||||
|
|
||||||
if d_line
|
if d_line
|
||||||
cd_diff = cd_diffs.diff_file_with_new_path(d_path)
|
cd_diff = cd_diffs.diff_file_with_new_path(d_path, d_mode)
|
||||||
|
|
||||||
c_path = cd_diff&.old_path || d_path
|
c_path = cd_diff&.old_path || d_path
|
||||||
|
c_mode = cd_diff&.a_mode || d_mode
|
||||||
c_line = LineMapper.new(cd_diff).new_to_old(d_line)
|
c_line = LineMapper.new(cd_diff).new_to_old(d_line)
|
||||||
|
|
||||||
if c_line
|
if c_line
|
||||||
|
@ -98,7 +102,7 @@ module Gitlab
|
||||||
else
|
else
|
||||||
# If the line is no longer in the MR, we unfortunately cannot show
|
# If the line is no longer in the MR, we unfortunately cannot show
|
||||||
# the current state on the CD diff, so we treat it as outdated.
|
# the current state on the CD diff, so we treat it as outdated.
|
||||||
ac_diff = ac_diffs.diff_file_with_new_path(c_path)
|
ac_diff = ac_diffs.diff_file_with_new_path(c_path, c_mode)
|
||||||
|
|
||||||
{ position: new_position(ac_diff, nil, c_line), outdated: true }
|
{ position: new_position(ac_diff, nil, c_line), outdated: true }
|
||||||
end
|
end
|
||||||
|
@ -115,22 +119,26 @@ module Gitlab
|
||||||
def trace_removed_line(position)
|
def trace_removed_line(position)
|
||||||
a_path = position.old_path
|
a_path = position.old_path
|
||||||
a_line = position.old_line
|
a_line = position.old_line
|
||||||
|
diff_file = diff_file(position)
|
||||||
|
a_mode = diff_file&.a_mode
|
||||||
|
|
||||||
ac_diff = ac_diffs.diff_file_with_old_path(a_path)
|
ac_diff = ac_diffs.diff_file_with_old_path(a_path, a_mode)
|
||||||
|
|
||||||
c_path = ac_diff&.new_path || a_path
|
c_path = ac_diff&.new_path || a_path
|
||||||
|
c_mode = ac_diff&.b_mode || a_mode
|
||||||
c_line = LineMapper.new(ac_diff).old_to_new(a_line)
|
c_line = LineMapper.new(ac_diff).old_to_new(a_line)
|
||||||
|
|
||||||
if c_line
|
if c_line
|
||||||
cd_diff = cd_diffs.diff_file_with_old_path(c_path)
|
cd_diff = cd_diffs.diff_file_with_old_path(c_path, c_mode)
|
||||||
|
|
||||||
d_path = cd_diff&.new_path || c_path
|
d_path = cd_diff&.new_path || c_path
|
||||||
|
d_mode = cd_diff&.b_mode || c_mode
|
||||||
d_line = LineMapper.new(cd_diff).old_to_new(c_line)
|
d_line = LineMapper.new(cd_diff).old_to_new(c_line)
|
||||||
|
|
||||||
if d_line
|
if d_line
|
||||||
# If the line is still in C but also in D, it has turned from a
|
# If the line is still in C but also in D, it has turned from a
|
||||||
# removed line into an unchanged one.
|
# removed line into an unchanged one.
|
||||||
bd_diff = bd_diffs.diff_file_with_new_path(d_path)
|
bd_diff = bd_diffs.diff_file_with_new_path(d_path, d_mode)
|
||||||
|
|
||||||
{ position: new_position(bd_diff, nil, d_line), outdated: true }
|
{ position: new_position(bd_diff, nil, d_line), outdated: true }
|
||||||
else
|
else
|
||||||
|
@ -148,17 +156,21 @@ module Gitlab
|
||||||
a_line = position.old_line
|
a_line = position.old_line
|
||||||
b_path = position.new_path
|
b_path = position.new_path
|
||||||
b_line = position.new_line
|
b_line = position.new_line
|
||||||
|
diff_file = diff_file(position)
|
||||||
|
a_mode = diff_file&.a_mode
|
||||||
|
b_mode = diff_file&.b_mode
|
||||||
|
|
||||||
ac_diff = ac_diffs.diff_file_with_old_path(a_path)
|
ac_diff = ac_diffs.diff_file_with_old_path(a_path, a_mode)
|
||||||
|
|
||||||
c_path = ac_diff&.new_path || a_path
|
c_path = ac_diff&.new_path || a_path
|
||||||
|
c_mode = ac_diff&.b_mode || a_mode
|
||||||
c_line = LineMapper.new(ac_diff).old_to_new(a_line)
|
c_line = LineMapper.new(ac_diff).old_to_new(a_line)
|
||||||
|
|
||||||
bd_diff = bd_diffs.diff_file_with_old_path(b_path)
|
bd_diff = bd_diffs.diff_file_with_old_path(b_path, b_mode)
|
||||||
|
|
||||||
d_line = LineMapper.new(bd_diff).old_to_new(b_line)
|
d_line = LineMapper.new(bd_diff).old_to_new(b_line)
|
||||||
|
|
||||||
cd_diff = cd_diffs.diff_file_with_old_path(c_path)
|
cd_diff = cd_diffs.diff_file_with_old_path(c_path, c_mode)
|
||||||
|
|
||||||
if c_line && d_line
|
if c_line && d_line
|
||||||
# If the line is still in C and D, it is still unchanged.
|
# If the line is still in C and D, it is still unchanged.
|
||||||
|
|
|
@ -15,28 +15,9 @@ module Gitlab
|
||||||
oid: oid,
|
oid: oid,
|
||||||
limit: limit
|
limit: limit
|
||||||
)
|
)
|
||||||
response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_blob, request, timeout: GitalyClient.fast_timeout)
|
GitalyClient.streaming_call(@gitaly_repo.storage_name, :blob_service, :get_blob, request, timeout: GitalyClient.fast_timeout) do |response|
|
||||||
|
consume_blob_response(response)
|
||||||
data = []
|
|
||||||
blob = nil
|
|
||||||
response.each do |msg|
|
|
||||||
if blob.nil?
|
|
||||||
blob = msg
|
|
||||||
end
|
|
||||||
|
|
||||||
data << msg.data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return if blob.oid.blank?
|
|
||||||
|
|
||||||
data = data.join
|
|
||||||
|
|
||||||
Gitlab::Git::Blob.new(
|
|
||||||
id: blob.oid,
|
|
||||||
size: blob.size,
|
|
||||||
data: data,
|
|
||||||
binary: Gitlab::Git::Blob.binary?(data)
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def batch_lfs_pointers(blob_ids)
|
def batch_lfs_pointers(blob_ids)
|
||||||
|
@ -47,9 +28,9 @@ module Gitlab
|
||||||
blob_ids: blob_ids
|
blob_ids: blob_ids
|
||||||
)
|
)
|
||||||
|
|
||||||
response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_lfs_pointers, request, timeout: GitalyClient.medium_timeout)
|
GitalyClient.streaming_call(@gitaly_repo.storage_name, :blob_service, :get_lfs_pointers, request, timeout: GitalyClient.medium_timeout) do |response|
|
||||||
|
map_lfs_pointers(response)
|
||||||
map_lfs_pointers(response)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_blobs(revision_paths, limit = -1)
|
def get_blobs(revision_paths, limit = -1)
|
||||||
|
@ -65,15 +46,15 @@ module Gitlab
|
||||||
limit: limit
|
limit: limit
|
||||||
)
|
)
|
||||||
|
|
||||||
response = GitalyClient.call(
|
GitalyClient.streaming_call(
|
||||||
@gitaly_repo.storage_name,
|
@gitaly_repo.storage_name,
|
||||||
:blob_service,
|
:blob_service,
|
||||||
:get_blobs,
|
:get_blobs,
|
||||||
request,
|
request,
|
||||||
timeout: GitalyClient.fast_timeout
|
timeout: GitalyClient.fast_timeout
|
||||||
)
|
) do |response|
|
||||||
|
GitalyClient::BlobsStitcher.new(response)
|
||||||
GitalyClient::BlobsStitcher.new(response)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_blob_types(revision_paths, limit = -1)
|
def get_blob_types(revision_paths, limit = -1)
|
||||||
|
@ -89,15 +70,15 @@ module Gitlab
|
||||||
limit: limit
|
limit: limit
|
||||||
)
|
)
|
||||||
|
|
||||||
response = GitalyClient.call(
|
GitalyClient.streaming_call(
|
||||||
@gitaly_repo.storage_name,
|
@gitaly_repo.storage_name,
|
||||||
:blob_service,
|
:blob_service,
|
||||||
:get_blobs,
|
:get_blobs,
|
||||||
request,
|
request,
|
||||||
timeout: GitalyClient.fast_timeout
|
timeout: GitalyClient.fast_timeout
|
||||||
)
|
) do |response|
|
||||||
|
map_blob_types(response)
|
||||||
map_blob_types(response)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_new_lfs_pointers(revision, limit, not_in, dynamic_timeout = nil)
|
def get_new_lfs_pointers(revision, limit, not_in, dynamic_timeout = nil)
|
||||||
|
@ -120,15 +101,15 @@ module Gitlab
|
||||||
GitalyClient.medium_timeout
|
GitalyClient.medium_timeout
|
||||||
end
|
end
|
||||||
|
|
||||||
response = GitalyClient.call(
|
GitalyClient.streaming_call(
|
||||||
@gitaly_repo.storage_name,
|
@gitaly_repo.storage_name,
|
||||||
:blob_service,
|
:blob_service,
|
||||||
:get_new_lfs_pointers,
|
:get_new_lfs_pointers,
|
||||||
request,
|
request,
|
||||||
timeout: timeout
|
timeout: timeout
|
||||||
)
|
) do |response|
|
||||||
|
map_lfs_pointers(response)
|
||||||
map_lfs_pointers(response)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_all_lfs_pointers
|
def get_all_lfs_pointers
|
||||||
|
@ -136,13 +117,36 @@ module Gitlab
|
||||||
repository: @gitaly_repo
|
repository: @gitaly_repo
|
||||||
)
|
)
|
||||||
|
|
||||||
response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_all_lfs_pointers, request, timeout: GitalyClient.medium_timeout)
|
GitalyClient.streaming_call(@gitaly_repo.storage_name, :blob_service, :get_all_lfs_pointers, request, timeout: GitalyClient.medium_timeout) do |response|
|
||||||
|
map_lfs_pointers(response)
|
||||||
map_lfs_pointers(response)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def consume_blob_response(response)
|
||||||
|
data = []
|
||||||
|
blob = nil
|
||||||
|
response.each do |msg|
|
||||||
|
if blob.nil?
|
||||||
|
blob = msg
|
||||||
|
end
|
||||||
|
|
||||||
|
data << msg.data
|
||||||
|
end
|
||||||
|
|
||||||
|
return if blob.oid.blank?
|
||||||
|
|
||||||
|
data = data.join
|
||||||
|
|
||||||
|
Gitlab::Git::Blob.new(
|
||||||
|
id: blob.oid,
|
||||||
|
size: blob.size,
|
||||||
|
data: data,
|
||||||
|
binary: Gitlab::Git::Blob.binary?(data)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def map_lfs_pointers(response)
|
def map_lfs_pointers(response)
|
||||||
response.flat_map do |message|
|
response.flat_map do |message|
|
||||||
message.lfs_pointers.map do |lfs_pointer|
|
message.lfs_pointers.map do |lfs_pointer|
|
||||||
|
|
|
@ -20,20 +20,20 @@ module Gitlab
|
||||||
when DashboardProcessingError
|
when DashboardProcessingError
|
||||||
error(error.message, :unprocessable_entity)
|
error(error.message, :unprocessable_entity)
|
||||||
when NOT_FOUND_ERROR
|
when NOT_FOUND_ERROR
|
||||||
error("#{dashboard_path} could not be found.", :not_found)
|
error(_("%{dashboard_path} could not be found.") % { dashboard_path: dashboard_path }, :not_found)
|
||||||
when PanelNotFoundError
|
when PanelNotFoundError
|
||||||
error(error.message, :not_found)
|
error(error.message, :not_found)
|
||||||
when ::Grafana::Client::Error
|
when ::Grafana::Client::Error
|
||||||
error(error.message, :service_unavailable)
|
error(error.message, :service_unavailable)
|
||||||
when MissingIntegrationError
|
when MissingIntegrationError
|
||||||
error('Proxy support for this API is not available currently', :bad_request)
|
error(_('Proxy support for this API is not available currently'), :bad_request)
|
||||||
else
|
else
|
||||||
raise error
|
raise error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def panels_not_found!(opts)
|
def panels_not_found!(opts)
|
||||||
raise PanelNotFoundError.new("No panels matching properties #{opts}")
|
raise PanelNotFoundError.new(_("No panels matching properties %{opts}") % { opts: opts })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ module Gitlab
|
||||||
module Stages
|
module Stages
|
||||||
class MetricEndpointInserter < BaseStage
|
class MetricEndpointInserter < BaseStage
|
||||||
def transform!
|
def transform!
|
||||||
raise Errors::DashboardProcessingError.new('Environment is required for Stages::MetricEndpointInserter') unless params[:environment]
|
raise Errors::DashboardProcessingError.new(_('Environment is required for Stages::MetricEndpointInserter')) unless params[:environment]
|
||||||
|
|
||||||
for_metrics do |metric|
|
for_metrics do |metric|
|
||||||
metric[:prometheus_endpoint_path] = endpoint_for_metric(metric)
|
metric[:prometheus_endpoint_path] = endpoint_for_metric(metric)
|
||||||
|
@ -33,7 +33,11 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def query_type(metric)
|
def query_type(metric)
|
||||||
metric[:query] ? :query : :query_range
|
if metric[:query]
|
||||||
|
::Prometheus::ProxyService::PROMETHEUS_QUERY_API.to_sym
|
||||||
|
else
|
||||||
|
::Prometheus::ProxyService::PROMETHEUS_QUERY_RANGE_API.to_sym
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def query_for_metric(metric)
|
def query_for_metric(metric)
|
||||||
|
|
|
@ -326,6 +326,9 @@ msgstr[1] ""
|
||||||
msgid "%{count} related %{pluralized_subject}: %{links}"
|
msgid "%{count} related %{pluralized_subject}: %{links}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "%{dashboard_path} could not be found."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "%{days} days until tags are automatically removed"
|
msgid "%{days} days until tags are automatically removed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -4669,9 +4672,15 @@ msgstr ""
|
||||||
msgid "Cluster does not exist"
|
msgid "Cluster does not exist"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Cluster is required for Stages::ClusterEndpointInserter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cluster level"
|
msgid "Cluster level"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Cluster type must be specificed for Stages::ClusterEndpointInserter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}."
|
msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -7070,6 +7079,9 @@ msgstr ""
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Dashboard uid not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "DashboardProjects|All"
|
msgid "DashboardProjects|All"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -7094,6 +7106,9 @@ msgstr ""
|
||||||
msgid "Data is still calculating..."
|
msgid "Data is still calculating..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Datasource name not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -8555,6 +8570,9 @@ msgstr ""
|
||||||
msgid "Environment does not have deployments"
|
msgid "Environment does not have deployments"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environment is required for Stages::MetricEndpointInserter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Environment is required for Stages::VariableEndpointInserter"
|
msgid "Environment is required for Stages::VariableEndpointInserter"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -10967,6 +10985,9 @@ msgstr ""
|
||||||
msgid "Grafana URL"
|
msgid "Grafana URL"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Grafana response contains invalid json"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "GrafanaIntegration|API Token"
|
msgid "GrafanaIntegration|API Token"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -11084,6 +11105,9 @@ msgstr ""
|
||||||
msgid "Group info:"
|
msgid "Group info:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Group is required when cluster_type is :group"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Group maintainers can register group runners in the %{link}"
|
msgid "Group maintainers can register group runners in the %{link}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -15068,6 +15092,9 @@ msgstr ""
|
||||||
msgid "No other labels with such name or description"
|
msgid "No other labels with such name or description"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No panels matching properties %{opts}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "No parent group"
|
msgid "No parent group"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -15994,6 +16021,9 @@ msgstr ""
|
||||||
msgid "Parent epic is not present."
|
msgid "Parent epic is not present."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Parsing error for param :embed_json. %{message}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Part of merge request changes"
|
msgid "Part of merge request changes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -17299,6 +17329,9 @@ msgstr ""
|
||||||
msgid "Project has too many %{label_for_message} to search"
|
msgid "Project has too many %{label_for_message} to search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Project is required when cluster_type is :project"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Project members"
|
msgid "Project members"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -17416,9 +17449,6 @@ msgstr ""
|
||||||
msgid "ProjectService|Comment"
|
msgid "ProjectService|Comment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Comment will be posted on each event"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "ProjectService|Perform common operations on GitLab project: %{project_name}"
|
msgid "ProjectService|Perform common operations on GitLab project: %{project_name}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -18211,6 +18241,9 @@ msgstr ""
|
||||||
msgid "Provider"
|
msgid "Provider"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Proxy support for this API is not available currently"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Pseudonymizer data collection"
|
msgid "Pseudonymizer data collection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -24212,6 +24245,9 @@ msgstr ""
|
||||||
msgid "Unreachable"
|
msgid "Unreachable"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Unrecognized cluster type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Unresolve"
|
msgid "Unresolve"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -262,6 +262,17 @@ RSpec.describe ProjectsFinder, :do_not_mock_admin_mode do
|
||||||
it { is_expected.to match_array([public_project]) }
|
it { is_expected.to match_array([public_project]) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'filter by repository_storage' do
|
||||||
|
let(:params) { { repository_storage: 'nfs-05' } }
|
||||||
|
let!(:project) { create(:project, :public) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.update_columns(repository_storage: 'nfs-05')
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to match_array([project]) }
|
||||||
|
end
|
||||||
|
|
||||||
describe 'sorting' do
|
describe 'sorting' do
|
||||||
let(:params) { { sort: 'name_asc' } }
|
let(:params) { { sort: 'name_asc' } }
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,20 @@ describe('DynamicField', () => {
|
||||||
.text(),
|
.text(),
|
||||||
).toBe(defaultProps.help);
|
).toBe(defaultProps.help);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders description with help text as HTML', () => {
|
||||||
|
const helpHTML = 'The <strong>URL</strong> of the project';
|
||||||
|
|
||||||
|
createComponent({
|
||||||
|
help: helpHTML,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
findGlFormGroup()
|
||||||
|
.find('small')
|
||||||
|
.html(),
|
||||||
|
).toContain(helpHTML);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('label text', () => {
|
describe('label text', () => {
|
||||||
|
|
|
@ -7,9 +7,4 @@ describe ServicesHelper do
|
||||||
it { expect(event_action_title('comment')).to eq 'Comment' }
|
it { expect(event_action_title('comment')).to eq 'Comment' }
|
||||||
it { expect(event_action_title('something')).to eq 'Something' }
|
it { expect(event_action_title('something')).to eq 'Something' }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'event_action_description' do
|
|
||||||
it { expect(event_action_description('comment')).to eq 'Comment will be posted on each event' }
|
|
||||||
it { expect(event_action_description('something')).to eq nil }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -234,5 +234,118 @@ describe Gitlab::Diff::PositionTracer::ImageStrategy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'symlink scenarios' do
|
||||||
|
let(:new_file) { old_file_status == :new }
|
||||||
|
let(:deleted_file) { old_file_status == :deleted }
|
||||||
|
let(:renamed_file) { old_file_status == :renamed }
|
||||||
|
|
||||||
|
let(:file_identifier) { "#{file_name}-#{new_file}-#{deleted_file}-#{renamed_file}" }
|
||||||
|
let(:file_identifier_hash) { Digest::SHA1.hexdigest(file_identifier) }
|
||||||
|
let(:old_position) { position(old_path: file_name, new_path: file_name, position_type: 'image', file_identifier_hash: file_identifier_hash) }
|
||||||
|
|
||||||
|
let(:update_file_commit) do
|
||||||
|
initial_commit
|
||||||
|
|
||||||
|
update_file(
|
||||||
|
branch_name,
|
||||||
|
file_name,
|
||||||
|
Base64.encode64('morecontent')
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:delete_file_commit) do
|
||||||
|
initial_commit
|
||||||
|
|
||||||
|
delete_file(branch_name, file_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:create_second_file_commit) do
|
||||||
|
initial_commit
|
||||||
|
|
||||||
|
create_file(
|
||||||
|
branch_name,
|
||||||
|
second_file_name,
|
||||||
|
Base64.encode64('morecontent')
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_feature_flags(file_identifier_hash: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'from symlink to image' do
|
||||||
|
let(:initial_commit) { project.commit('a19c7f9a147e35e535c797cf148d29c24dac5544') }
|
||||||
|
let(:symlink_to_image_commit) { project.commit('8cfca8420812e5bd7479aa32cf33e0c95a3ca576') }
|
||||||
|
let(:branch_name) { 'diff-files-symlink-to-image' }
|
||||||
|
let(:file_name) { 'symlink-to-image.png' }
|
||||||
|
|
||||||
|
context "when the old position is on the new image file" do
|
||||||
|
let(:old_file_status) { :new }
|
||||||
|
|
||||||
|
context "when the image file's content was unchanged between the old and the new diff" do
|
||||||
|
let(:old_diff_refs) { diff_refs(initial_commit, symlink_to_image_commit) }
|
||||||
|
let(:new_diff_refs) { diff_refs(initial_commit, create_second_file_commit) }
|
||||||
|
|
||||||
|
it "returns the new position" do
|
||||||
|
expect_new_position(
|
||||||
|
old_path: file_name,
|
||||||
|
new_path: file_name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the image file's content was changed between the old and the new diff" do
|
||||||
|
let(:old_diff_refs) { diff_refs(initial_commit, symlink_to_image_commit) }
|
||||||
|
let(:new_diff_refs) { diff_refs(initial_commit, update_file_commit) }
|
||||||
|
let(:change_diff_refs) { diff_refs(symlink_to_image_commit, update_file_commit) }
|
||||||
|
|
||||||
|
it "returns the position of the change" do
|
||||||
|
expect_change_position(
|
||||||
|
old_path: file_name,
|
||||||
|
new_path: file_name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the image file was removed between the old and the new diff" do
|
||||||
|
let(:old_diff_refs) { diff_refs(initial_commit, symlink_to_image_commit) }
|
||||||
|
let(:new_diff_refs) { diff_refs(initial_commit, delete_file_commit) }
|
||||||
|
let(:change_diff_refs) { diff_refs(symlink_to_image_commit, delete_file_commit) }
|
||||||
|
|
||||||
|
it "returns the position of the change" do
|
||||||
|
expect_change_position(
|
||||||
|
old_path: file_name,
|
||||||
|
new_path: file_name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'from image to symlink' do
|
||||||
|
let(:initial_commit) { project.commit('d10dcdfbbb2b59a959a5f5d66a4adf28f0ea4008') }
|
||||||
|
let(:image_to_symlink_commit) { project.commit('3e94fdaa60da8aed38401b91bc56be70d54ca424') }
|
||||||
|
let(:branch_name) { 'diff-files-image-to-symlink' }
|
||||||
|
let(:file_name) { 'image-to-symlink.png' }
|
||||||
|
|
||||||
|
context "when the old position is on the added image file" do
|
||||||
|
let(:old_file_status) { :new }
|
||||||
|
|
||||||
|
context "when the image file gets changed to a symlink between the old and the new diff" do
|
||||||
|
let(:old_diff_refs) { diff_refs(initial_commit.parent, initial_commit) }
|
||||||
|
let(:new_diff_refs) { diff_refs(initial_commit.parent, image_to_symlink_commit) }
|
||||||
|
let(:change_diff_refs) { diff_refs(initial_commit, image_to_symlink_commit) }
|
||||||
|
|
||||||
|
it "returns the position of the change" do
|
||||||
|
expect_change_position(
|
||||||
|
old_path: file_name,
|
||||||
|
new_path: file_name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1801,5 +1801,143 @@ describe Gitlab::Diff::PositionTracer::LineStrategy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'symlink scenarios' do
|
||||||
|
let(:new_file) { old_file_status == :new }
|
||||||
|
let(:deleted_file) { old_file_status == :deleted }
|
||||||
|
let(:renamed_file) { old_file_status == :renamed }
|
||||||
|
|
||||||
|
let(:file_identifier) { "#{file_name}-#{new_file}-#{deleted_file}-#{renamed_file}" }
|
||||||
|
let(:file_identifier_hash) { Digest::SHA1.hexdigest(file_identifier) }
|
||||||
|
|
||||||
|
let(:update_line_commit) do
|
||||||
|
update_file(
|
||||||
|
branch_name,
|
||||||
|
file_name,
|
||||||
|
<<-CONTENT.strip_heredoc
|
||||||
|
A
|
||||||
|
BB
|
||||||
|
C
|
||||||
|
CONTENT
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:delete_file_commit) do
|
||||||
|
delete_file(branch_name, file_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:create_second_file_commit) do
|
||||||
|
create_file(
|
||||||
|
branch_name,
|
||||||
|
second_file_name,
|
||||||
|
<<-CONTENT.strip_heredoc
|
||||||
|
D
|
||||||
|
E
|
||||||
|
CONTENT
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_feature_flags(file_identifier_hash: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'from symlink to text' do
|
||||||
|
let(:initial_commit) { project.commit('0e5b363105e9176a77bac94d7ff6d8c4fb35c3eb') }
|
||||||
|
let(:symlink_to_text_commit) { project.commit('689815e617abc6889f1fded4834d2dd7d942a58e') }
|
||||||
|
let(:branch_name) { 'diff-files-symlink-to-text' }
|
||||||
|
let(:file_name) { 'symlink-to-text.txt' }
|
||||||
|
let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 3, file_identifier_hash: file_identifier_hash) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create_branch('diff-files-symlink-to-text-test', branch_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the old position is on the new text file" do
|
||||||
|
let(:old_file_status) { :new }
|
||||||
|
|
||||||
|
context "when the text file's content was unchanged between the old and the new diff" do
|
||||||
|
let(:old_diff_refs) { diff_refs(initial_commit, symlink_to_text_commit) }
|
||||||
|
let(:new_diff_refs) { diff_refs(initial_commit, create_second_file_commit) }
|
||||||
|
|
||||||
|
it "returns the new position" do
|
||||||
|
expect_new_position(
|
||||||
|
new_path: old_position.new_path,
|
||||||
|
new_line: old_position.new_line
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the text file's content has change, but the line was unchanged between the old and the new diff" do
|
||||||
|
let(:old_diff_refs) { diff_refs(initial_commit, symlink_to_text_commit) }
|
||||||
|
let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
|
||||||
|
|
||||||
|
it "returns the new position" do
|
||||||
|
expect_new_position(
|
||||||
|
new_path: old_position.new_path,
|
||||||
|
new_line: old_position.new_line
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the text file's line was changed between the old and the new diff" do
|
||||||
|
let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 2, file_identifier_hash: file_identifier_hash) }
|
||||||
|
|
||||||
|
let(:old_diff_refs) { diff_refs(initial_commit, symlink_to_text_commit) }
|
||||||
|
let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
|
||||||
|
let(:change_diff_refs) { diff_refs(symlink_to_text_commit, update_line_commit) }
|
||||||
|
|
||||||
|
it "returns the position of the change" do
|
||||||
|
expect_change_position(
|
||||||
|
old_path: file_name,
|
||||||
|
new_path: file_name,
|
||||||
|
old_line: 2,
|
||||||
|
new_line: nil
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the text file was removed between the old and the new diff" do
|
||||||
|
let(:old_diff_refs) { diff_refs(initial_commit, symlink_to_text_commit) }
|
||||||
|
let(:new_diff_refs) { diff_refs(initial_commit, delete_file_commit) }
|
||||||
|
let(:change_diff_refs) { diff_refs(symlink_to_text_commit, delete_file_commit) }
|
||||||
|
|
||||||
|
it "returns the position of the change" do
|
||||||
|
expect_change_position(
|
||||||
|
old_path: file_name,
|
||||||
|
new_path: file_name,
|
||||||
|
old_line: 3,
|
||||||
|
new_line: nil
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'from text to symlink' do
|
||||||
|
let(:initial_commit) { project.commit('3db7bd90bab8ce8f02c9818590b84739a2e97230') }
|
||||||
|
let(:text_to_symlink_commit) { project.commit('5e2c2708c2e403dece5dd25759369150aac51644') }
|
||||||
|
let(:branch_name) { 'diff-files-text-to-symlink' }
|
||||||
|
let(:file_name) { 'text-to-symlink.txt' }
|
||||||
|
|
||||||
|
context "when the position is on the added text file" do
|
||||||
|
let(:old_file_status) { :new }
|
||||||
|
|
||||||
|
context "when the text file gets changed to a symlink between the old and the new diff" do
|
||||||
|
let(:old_diff_refs) { diff_refs(initial_commit.parent, initial_commit) }
|
||||||
|
let(:new_diff_refs) { diff_refs(initial_commit.parent, text_to_symlink_commit) }
|
||||||
|
let(:change_diff_refs) { diff_refs(initial_commit, text_to_symlink_commit) }
|
||||||
|
|
||||||
|
it "returns the position of the change" do
|
||||||
|
expect_change_position(
|
||||||
|
old_path: file_name,
|
||||||
|
new_path: file_name,
|
||||||
|
old_line: 3,
|
||||||
|
new_line: nil
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,8 +47,8 @@ describe Ci::Bridge do
|
||||||
CI_JOB_NAME CI_JOB_STAGE CI_COMMIT_SHA CI_COMMIT_SHORT_SHA
|
CI_JOB_NAME CI_JOB_STAGE CI_COMMIT_SHA CI_COMMIT_SHORT_SHA
|
||||||
CI_COMMIT_BEFORE_SHA CI_COMMIT_REF_NAME CI_COMMIT_REF_SLUG
|
CI_COMMIT_BEFORE_SHA CI_COMMIT_REF_NAME CI_COMMIT_REF_SLUG
|
||||||
CI_PROJECT_ID CI_PROJECT_NAME CI_PROJECT_PATH
|
CI_PROJECT_ID CI_PROJECT_NAME CI_PROJECT_PATH
|
||||||
CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PIPELINE_IID
|
CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PROJECT_ROOT_NAMESPACE
|
||||||
CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE
|
CI_PIPELINE_IID CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE
|
||||||
CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION CI_COMMIT_REF_PROTECTED
|
CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION CI_COMMIT_REF_PROTECTED
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -2380,6 +2380,7 @@ describe Ci::Build do
|
||||||
{ key: 'CI_PROJECT_PATH', value: project.full_path, public: true, masked: false },
|
{ key: 'CI_PROJECT_PATH', value: project.full_path, public: true, masked: false },
|
||||||
{ key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true, masked: false },
|
{ key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true, masked: false },
|
||||||
{ key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true, masked: false },
|
{ key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true, masked: false },
|
||||||
|
{ key: 'CI_PROJECT_ROOT_NAMESPACE', value: project.namespace.root_ancestor.path, public: true, masked: false },
|
||||||
{ key: 'CI_PROJECT_URL', value: project.web_url, public: true, masked: false },
|
{ key: 'CI_PROJECT_URL', value: project.web_url, public: true, masked: false },
|
||||||
{ key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true, masked: false },
|
{ key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true, masked: false },
|
||||||
{ key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: project.repository_languages.map(&:name).join(',').downcase, public: true, masked: false },
|
{ key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: project.repository_languages.map(&:name).join(',').downcase, public: true, masked: false },
|
||||||
|
|
|
@ -130,6 +130,24 @@ describe GlobalPolicy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'using project statistics filters' do
|
||||||
|
context 'regular user' do
|
||||||
|
it { is_expected.not_to be_allowed(:use_project_statistics_filters) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'admin' do
|
||||||
|
let(:current_user) { create(:user, :admin) }
|
||||||
|
|
||||||
|
context 'when admin mode is enabled', :enable_admin_mode do
|
||||||
|
it { is_expected.to be_allowed(:use_project_statistics_filters) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when admin mode is disabled' do
|
||||||
|
it { is_expected.to be_disallowed(:use_project_statistics_filters) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
shared_examples 'access allowed when terms accepted' do |ability|
|
shared_examples 'access allowed when terms accepted' do |ability|
|
||||||
it { is_expected.not_to be_allowed(ability) }
|
it { is_expected.not_to be_allowed(ability) }
|
||||||
|
|
||||||
|
|
|
@ -584,6 +584,85 @@ describe API::Projects do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'sorting by project statistics' do
|
||||||
|
%w(repository_size storage_size wiki_size).each do |order_by|
|
||||||
|
context "sorting by #{order_by}" do
|
||||||
|
before do
|
||||||
|
ProjectStatistics.update_all(order_by => 100)
|
||||||
|
project4.statistics.update_columns(order_by => 10)
|
||||||
|
project.statistics.update_columns(order_by => 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'admin user' do
|
||||||
|
let(:current_user) { admin }
|
||||||
|
|
||||||
|
context "when sorting by #{order_by} ascendingly" do
|
||||||
|
it 'returns a properly sorted list of projects' do
|
||||||
|
get api('/projects', current_user), params: { order_by: order_by, sort: :asc }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(response).to include_pagination_headers
|
||||||
|
expect(json_response).to be_an Array
|
||||||
|
expect(json_response.first['id']).to eq(project4.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when sorting by #{order_by} descendingly" do
|
||||||
|
it 'returns a properly sorted list of projects' do
|
||||||
|
get api('/projects', current_user), params: { order_by: order_by, sort: :desc }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(response).to include_pagination_headers
|
||||||
|
expect(json_response).to be_an Array
|
||||||
|
expect(json_response.first['id']).to eq(project.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'non-admin user' do
|
||||||
|
let(:current_user) { user }
|
||||||
|
let(:projects) { [public_project, project, project2, project3] }
|
||||||
|
|
||||||
|
it 'returns projects ordered normally' do
|
||||||
|
get api('/projects', current_user), params: { order_by: order_by }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(response).to include_pagination_headers
|
||||||
|
expect(json_response).to be_an Array
|
||||||
|
expect(json_response.map { |project| project['id'] }).to eq(projects.map(&:id).reverse)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'filtering by repository_storage' do
|
||||||
|
before do
|
||||||
|
[project, project3].each { |proj| proj.update_columns(repository_storage: 'nfs-11') }
|
||||||
|
# Since we don't actually have Gitaly configured with an nfs-11 storage, an error would be raised
|
||||||
|
# when we present the projects in a response, as we ask Gitaly for stuff like default branch and Gitaly
|
||||||
|
# is not configured for a nfs-11 storage. So we trick Rails into thinking the storage for these projects
|
||||||
|
# is still default (in reality, it is).
|
||||||
|
allow_any_instance_of(Project).to receive(:repository_storage).and_return('default')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'admin user' do
|
||||||
|
it_behaves_like 'projects response' do
|
||||||
|
let(:filter) { { repository_storage: 'nfs-11' } }
|
||||||
|
let(:current_user) { admin }
|
||||||
|
let(:projects) { [project, project3] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'non-admin user' do
|
||||||
|
it_behaves_like 'projects response' do
|
||||||
|
let(:filter) { { repository_storage: 'nfs-11' } }
|
||||||
|
let(:current_user) { user }
|
||||||
|
let(:projects) { [public_project, project, project2, project3] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'with keyset pagination' do
|
context 'with keyset pagination' do
|
||||||
let(:current_user) { user }
|
let(:current_user) { user }
|
||||||
let(:projects) { [public_project, project, project2, project3] }
|
let(:projects) { [public_project, project, project2, project3] }
|
||||||
|
|
|
@ -29,6 +29,10 @@ module TestEnv
|
||||||
'gitattributes' => '5a62481',
|
'gitattributes' => '5a62481',
|
||||||
'expand-collapse-diffs' => '4842455',
|
'expand-collapse-diffs' => '4842455',
|
||||||
'symlink-expand-diff' => '81e6355',
|
'symlink-expand-diff' => '81e6355',
|
||||||
|
'diff-files-symlink-to-image' => '8cfca84',
|
||||||
|
'diff-files-image-to-symlink' => '3e94fda',
|
||||||
|
'diff-files-symlink-to-text' => '689815e',
|
||||||
|
'diff-files-text-to-symlink' => '5e2c270',
|
||||||
'expand-collapse-files' => '025db92',
|
'expand-collapse-files' => '025db92',
|
||||||
'expand-collapse-lines' => '238e82d',
|
'expand-collapse-lines' => '238e82d',
|
||||||
'pages-deploy' => '7897d5b',
|
'pages-deploy' => '7897d5b',
|
||||||
|
|
Loading…
Reference in New Issue