Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
bbfd13e575
commit
84dd3070df
2
Gemfile
2
Gemfile
|
|
@ -57,7 +57,7 @@ gem 'gssapi', group: :kerberos
|
|||
# Spam and anti-bot protection
|
||||
gem 'recaptcha', '~> 4.11', require: 'recaptcha/rails'
|
||||
gem 'akismet', '~> 3.0'
|
||||
gem 'invisible_captcha', '~> 0.12.1'
|
||||
gem 'invisible_captcha', '~> 1.1.0'
|
||||
|
||||
# Two-factor authentication
|
||||
gem 'devise-two-factor', '~> 3.1.0'
|
||||
|
|
|
|||
|
|
@ -598,8 +598,8 @@ GEM
|
|||
i18n_data (0.8.0)
|
||||
icalendar (2.4.1)
|
||||
ice_nine (0.11.2)
|
||||
invisible_captcha (0.12.1)
|
||||
rails (>= 3.2.0)
|
||||
invisible_captcha (1.1.0)
|
||||
rails (>= 4.2)
|
||||
ipaddress (0.8.3)
|
||||
jaeger-client (1.1.0)
|
||||
opentracing (~> 0.3)
|
||||
|
|
@ -1395,7 +1395,7 @@ DEPENDENCIES
|
|||
html2text
|
||||
httparty (~> 0.16.4)
|
||||
icalendar
|
||||
invisible_captcha (~> 0.12.1)
|
||||
invisible_captcha (~> 1.1.0)
|
||||
ipaddress (~> 0.8.3)
|
||||
jira-ruby (~> 2.1.4)
|
||||
js_regex (~> 3.4)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { BLOB_EDITOR_ERROR, BLOB_PREVIEW_ERROR } from './constants';
|
|||
import TemplateSelectorMediator from '../blob/file_template_mediator';
|
||||
import { addEditorMarkdownListeners } from '~/lib/utils/text_markdown';
|
||||
import EditorLite from '~/editor/editor_lite';
|
||||
import { FileTemplateExtension } from '~/editor/editor_file_template_ext';
|
||||
import { FileTemplateExtension } from '~/editor/extensions/editor_file_template_ext';
|
||||
import { insertFinalNewline } from '~/lib/utils/text_utility';
|
||||
|
||||
export default class EditBlob {
|
||||
|
|
@ -16,7 +16,7 @@ export default class EditBlob {
|
|||
this.configureMonacoEditor();
|
||||
|
||||
if (this.options.isMarkdown) {
|
||||
import('~/editor/editor_markdown_ext')
|
||||
import('~/editor/extensions/editor_markdown_ext')
|
||||
.then(({ EditorMarkdownExtension: MarkdownExtension } = {}) => {
|
||||
this.editor.use(new MarkdownExtension());
|
||||
addEditorMarkdownListeners(this.editor);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Api from '~/api';
|
||||
import { registerSchema } from '~/ide/utils';
|
||||
import { EditorLiteExtension } from './editor_lite_extension_base';
|
||||
import { EXTENSION_CI_SCHEMA_FILE_NAME_MATCH } from './constants';
|
||||
import { EXTENSION_CI_SCHEMA_FILE_NAME_MATCH } from '../constants';
|
||||
|
||||
export class CiSchemaExtension extends EditorLiteExtension {
|
||||
/**
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { ERROR_INSTANCE_REQUIRED_FOR_EXTENSION } from './constants';
|
||||
import { ERROR_INSTANCE_REQUIRED_FOR_EXTENSION } from '../constants';
|
||||
|
||||
export class EditorLiteExtension {
|
||||
constructor({ instance, ...options } = {}) {
|
||||
|
|
@ -103,6 +103,7 @@ class GfmAutoComplete {
|
|||
at: '/',
|
||||
alias: 'commands',
|
||||
searchKey: 'search',
|
||||
limit: 100,
|
||||
skipSpecialCharacterTest: true,
|
||||
skipMarkdownCharacterTest: true,
|
||||
data: GfmAutoComplete.defaultLoadingData,
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ export default class Todos {
|
|||
goToTodoUrl(e) {
|
||||
const todoLink = this.dataset.url;
|
||||
|
||||
if (!todoLink || e.target.tagName === 'A' || e.target.tagName === 'IMG') {
|
||||
if (!todoLink || e.target.closest('a')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
render(createElement) {
|
||||
return createElement(PipelineSchedulesCallout);
|
||||
},
|
||||
provide: {
|
||||
docsUrl,
|
||||
illustrationUrl,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(PipelineSchedulesCallout);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import EditorLite from '~/vue_shared/components/editor_lite.vue';
|
||||
import { CiSchemaExtension } from '~/editor/editor_ci_schema_ext';
|
||||
import { CiSchemaExtension } from '~/editor/extensions/editor_ci_schema_ext';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import { getDateInPast } from '~/lib/utils/datetime_utility';
|
|||
import getPipelineCountByStatus from '../graphql/queries/get_pipeline_count_by_status.query.graphql';
|
||||
import getProjectPipelineStatistics from '../graphql/queries/get_project_pipeline_statistics.query.graphql';
|
||||
import StatisticsList from './statistics_list.vue';
|
||||
import PipelinesAreaChart from './pipelines_area_chart.vue';
|
||||
import CiCdAnalyticsAreaChart from './ci_cd_analytics_area_chart.vue';
|
||||
|
||||
import {
|
||||
CHART_CONTAINER_HEIGHT,
|
||||
CHART_DATE_FORMAT,
|
||||
|
|
@ -52,13 +53,19 @@ export default {
|
|||
GlColumnChart,
|
||||
GlSkeletonLoader,
|
||||
StatisticsList,
|
||||
PipelinesAreaChart,
|
||||
CiCdAnalyticsAreaChart,
|
||||
DeploymentFrequencyCharts: () =>
|
||||
import('ee_component/projects/pipelines/charts/components/deployment_frequency_charts.vue'),
|
||||
},
|
||||
inject: {
|
||||
projectPath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
shouldRenderDeploymentFrequencyCharts: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -260,6 +267,15 @@ export default {
|
|||
lastYear: __('Pipelines for last year'),
|
||||
};
|
||||
},
|
||||
areaChartOptions: {
|
||||
xAxis: {
|
||||
name: s__('Pipeline|Date'),
|
||||
type: 'category',
|
||||
},
|
||||
yAxis: {
|
||||
name: s__('Pipeline|Pipelines'),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
|
|
@ -292,12 +308,17 @@ export default {
|
|||
</div>
|
||||
<hr />
|
||||
<h4 class="gl-my-4">{{ __('Pipelines charts') }}</h4>
|
||||
<pipelines-area-chart
|
||||
<ci-cd-analytics-area-chart
|
||||
v-for="(chart, index) in areaCharts"
|
||||
:key="index"
|
||||
:chart-data="chart.data"
|
||||
:area-chart-options="$options.areaChartOptions"
|
||||
>
|
||||
{{ chart.title }}
|
||||
</pipelines-area-chart>
|
||||
</ci-cd-analytics-area-chart>
|
||||
<template v-if="shouldRenderDeploymentFrequencyCharts">
|
||||
<hr />
|
||||
<deployment-frequency-charts />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<script>
|
||||
import dateFormat from 'dateformat';
|
||||
import { GlColumnChart } from '@gitlab/ui/dist/charts';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import { getDateInPast } from '~/lib/utils/datetime_utility';
|
||||
import StatisticsList from './statistics_list.vue';
|
||||
import PipelinesAreaChart from './pipelines_area_chart.vue';
|
||||
import CiCdAnalyticsAreaChart from './ci_cd_analytics_area_chart.vue';
|
||||
|
||||
import {
|
||||
CHART_CONTAINER_HEIGHT,
|
||||
INNER_CHART_HEIGHT,
|
||||
|
|
@ -19,7 +20,15 @@ export default {
|
|||
components: {
|
||||
StatisticsList,
|
||||
GlColumnChart,
|
||||
PipelinesAreaChart,
|
||||
CiCdAnalyticsAreaChart,
|
||||
DeploymentFrequencyCharts: () =>
|
||||
import('ee_component/projects/pipelines/charts/components/deployment_frequency_charts.vue'),
|
||||
},
|
||||
inject: {
|
||||
shouldRenderDeploymentFrequencyCharts: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
counts: {
|
||||
|
|
@ -112,6 +121,15 @@ export default {
|
|||
lastYear: __('Pipelines for last year'),
|
||||
};
|
||||
},
|
||||
areaChartOptions: {
|
||||
xAxis: {
|
||||
name: s__('Pipeline|Date'),
|
||||
type: 'category',
|
||||
},
|
||||
yAxis: {
|
||||
name: s__('Pipeline|Pipelines'),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
|
|
@ -140,12 +158,17 @@ export default {
|
|||
</div>
|
||||
<hr />
|
||||
<h4 class="my-4">{{ __('Pipelines charts') }}</h4>
|
||||
<pipelines-area-chart
|
||||
<ci-cd-analytics-area-chart
|
||||
v-for="(chart, index) in areaCharts"
|
||||
:key="index"
|
||||
:chart-data="chart.data"
|
||||
:area-chart-options="$options.areaChartOptions"
|
||||
>
|
||||
{{ chart.title }}
|
||||
</pipelines-area-chart>
|
||||
</ci-cd-analytics-area-chart>
|
||||
<template v-if="shouldRenderDeploymentFrequencyCharts">
|
||||
<hr />
|
||||
<deployment-frequency-charts />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script>
|
||||
import { GlAreaChart } from '@gitlab/ui/dist/charts';
|
||||
import { s__ } from '~/locale';
|
||||
import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue';
|
||||
import { CHART_CONTAINER_HEIGHT } from '../constants';
|
||||
|
||||
export default {
|
||||
name: 'CiCdAnalyticsAreaChart',
|
||||
components: {
|
||||
GlAreaChart,
|
||||
ResizableChartContainer,
|
||||
|
|
@ -14,14 +14,9 @@ export default {
|
|||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
areaChartOptions: {
|
||||
xAxis: {
|
||||
name: s__('Pipeline|Date'),
|
||||
type: 'category',
|
||||
},
|
||||
yAxis: {
|
||||
name: s__('Pipeline|Pipelines'),
|
||||
areaChartOptions: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
chartContainerHeight: CHART_CONTAINER_HEIGHT,
|
||||
|
|
@ -39,7 +34,7 @@ export default {
|
|||
:height="$options.chartContainerHeight"
|
||||
:data="chartData"
|
||||
:include-legend-avg-max="false"
|
||||
:option="$options.areaChartOptions"
|
||||
:option="areaChartOptions"
|
||||
/>
|
||||
</resizable-chart-container>
|
||||
</div>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import ProjectPipelinesChartsLegacy from './components/app_legacy.vue';
|
||||
import ProjectPipelinesCharts from './components/app.vue';
|
||||
|
||||
|
|
@ -35,6 +36,10 @@ const mountPipelineChartsApp = (el) => {
|
|||
projectPath,
|
||||
} = el.dataset;
|
||||
|
||||
const shouldRenderDeploymentFrequencyCharts = parseBoolean(
|
||||
el.dataset.shouldRenderDeploymentFrequencyCharts,
|
||||
);
|
||||
|
||||
const parseAreaChartData = (labels, totals, success) => {
|
||||
let parsedData = {};
|
||||
|
||||
|
|
@ -61,6 +66,7 @@ const mountPipelineChartsApp = (el) => {
|
|||
apolloProvider,
|
||||
provide: {
|
||||
projectPath,
|
||||
shouldRenderDeploymentFrequencyCharts,
|
||||
},
|
||||
render: (createElement) => createElement(ProjectPipelinesCharts, {}),
|
||||
});
|
||||
|
|
@ -72,6 +78,10 @@ const mountPipelineChartsApp = (el) => {
|
|||
components: {
|
||||
ProjectPipelinesChartsLegacy,
|
||||
},
|
||||
provide: {
|
||||
projectPath,
|
||||
shouldRenderDeploymentFrequencyCharts,
|
||||
},
|
||||
render: (createElement) =>
|
||||
createElement(ProjectPipelinesChartsLegacy, {
|
||||
props: {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default {
|
|||
],
|
||||
},
|
||||
translations: {
|
||||
formLabel: s__('UserLists|Feature flag list'),
|
||||
formLabel: s__('UserLists|Feature flag user list'),
|
||||
formSubtitle: s__(
|
||||
'UserLists|Lists allow you to define a set of users to be used with feature flags. %{linkStart}Read more about feature flag lists.%{linkEnd}',
|
||||
),
|
||||
|
|
|
|||
|
|
@ -307,8 +307,7 @@ export default {
|
|||
callback: this.checkStatus,
|
||||
startingInterval: this.startingPollInterval,
|
||||
maxInterval: this.startingPollInterval + secondsToMilliseconds(4 * 60),
|
||||
hiddenInterval:
|
||||
window.gon?.features?.widgetVisibilityPolling && secondsToMilliseconds(6 * 60),
|
||||
hiddenInterval: secondsToMilliseconds(6 * 60),
|
||||
incrementByFactorOf: 2,
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ module InvisibleCaptchaOnSignup
|
|||
end
|
||||
|
||||
def on_honeypot_spam_callback
|
||||
return unless Feature.enabled?(:invisible_captcha)
|
||||
return unless Gitlab::CurrentSettings.invisible_captcha_enabled
|
||||
|
||||
invisible_captcha_honeypot_counter.increment
|
||||
log_request('Invisible_Captcha_Honeypot_Request')
|
||||
|
|
@ -17,7 +17,7 @@ module InvisibleCaptchaOnSignup
|
|||
end
|
||||
|
||||
def on_timestamp_spam_callback
|
||||
return unless Feature.enabled?(:invisible_captcha)
|
||||
return unless Gitlab::CurrentSettings.invisible_captcha_enabled
|
||||
|
||||
invisible_captcha_timestamp_counter.increment
|
||||
log_request('Invisible_Captcha_Timestamp_Request')
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
before_action :authenticate_user!, only: [:assign_related_issues]
|
||||
before_action :check_user_can_push_to_source_branch!, only: [:rebase]
|
||||
before_action only: [:show] do
|
||||
push_frontend_feature_flag(:widget_visibility_polling, @project, default_enabled: true)
|
||||
push_frontend_feature_flag(:mr_commit_neighbor_nav, @project, default_enabled: true)
|
||||
push_frontend_feature_flag(:multiline_comments, @project, default_enabled: true)
|
||||
push_frontend_feature_flag(:file_identifier_hash)
|
||||
|
|
|
|||
|
|
@ -242,6 +242,7 @@ module ApplicationSettingsHelper
|
|||
:housekeeping_incremental_repack_period,
|
||||
:html_emails_enabled,
|
||||
:import_sources,
|
||||
:invisible_captcha_enabled,
|
||||
:max_artifacts_size,
|
||||
:max_attachment_size,
|
||||
:max_import_size,
|
||||
|
|
|
|||
|
|
@ -22,4 +22,10 @@ module GraphHelper
|
|||
ratio = (counts[:success].to_f / (counts[:success] + counts[:failed])) * 100
|
||||
ratio.to_i
|
||||
end
|
||||
|
||||
def should_render_deployment_frequency_charts
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
GraphHelper.prepend_if_ee('EE::GraphHelper')
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ module PreferencesHelper
|
|||
|
||||
def integration_views
|
||||
[].tap do |views|
|
||||
views << { name: 'gitpod', message: gitpod_enable_description, message_url: 'https://gitpod.io/', help_link: help_page_path('integration/gitpod.md') } if Gitlab::Gitpod.feature_and_settings_enabled?
|
||||
views << { name: 'gitpod', message: gitpod_enable_description, message_url: 'https://gitpod.io/', help_link: help_page_path('integration/gitpod.md') } if Gitlab::CurrentSettings.gitpod_enabled
|
||||
views << { name: 'sourcegraph', message: sourcegraph_url_message, message_url: Gitlab::CurrentSettings.sourcegraph_url, help_link: help_page_path('user/profile/preferences.md', anchor: 'sourcegraph') } if Gitlab::Sourcegraph.feature_available? && Gitlab::CurrentSettings.sourcegraph_enabled
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ module WebIdeButtonHelper
|
|||
end
|
||||
|
||||
def show_gitpod_button?
|
||||
show_web_ide_button? && Gitlab::Gitpod.feature_and_settings_enabled?(@project)
|
||||
show_web_ide_button? && Gitlab::CurrentSettings.gitpod_enabled
|
||||
end
|
||||
|
||||
def can_push_code?
|
||||
|
|
@ -54,7 +54,7 @@ module WebIdeButtonHelper
|
|||
end
|
||||
|
||||
def gitpod_url
|
||||
return "" unless Gitlab::Gitpod.feature_and_settings_enabled?(@project)
|
||||
return "" unless Gitlab::CurrentSettings.gitpod_enabled
|
||||
|
||||
"#{Gitlab::CurrentSettings.gitpod_url}##{project_tree_url(@project, tree_join(@ref, @path || ''))}"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ class ApplicationSetting < ApplicationRecord
|
|||
validates :default_artifacts_expire_in, presence: true, duration: true
|
||||
|
||||
validates :container_expiration_policies_enable_historic_entries,
|
||||
inclusion: { in: [true, false], message: 'must be a boolean value' }
|
||||
inclusion: { in: [true, false], message: _('must be a boolean value') }
|
||||
|
||||
validates :container_registry_token_expire_delay,
|
||||
presence: true,
|
||||
|
|
@ -309,6 +309,9 @@ class ApplicationSetting < ApplicationRecord
|
|||
validates :container_registry_expiration_policies_worker_capacity,
|
||||
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
||||
|
||||
validates :invisible_captcha_enabled,
|
||||
inclusion: { in: [true, false], message: _('must be a boolean value') }
|
||||
|
||||
SUPPORTED_KEY_TYPES.each do |type|
|
||||
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
|
||||
end
|
||||
|
|
@ -469,7 +472,7 @@ class ApplicationSetting < ApplicationRecord
|
|||
attr_encrypted :cloud_license_auth_token, encryption_options_base_truncated_aes_256_gcm
|
||||
|
||||
validates :disable_feed_token,
|
||||
inclusion: { in: [true, false], message: 'must be a boolean value' }
|
||||
inclusion: { in: [true, false], message: _('must be a boolean value') }
|
||||
|
||||
before_validation :ensure_uuid!
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ module ApplicationSettingImplementation
|
|||
housekeeping_gc_period: 200,
|
||||
housekeeping_incremental_repack_period: 10,
|
||||
import_sources: Settings.gitlab['import_sources'],
|
||||
invisible_captcha_enabled: false,
|
||||
issues_create_limit: 300,
|
||||
local_markdown_version: 0,
|
||||
login_recaptcha_protection_enabled: false,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Boards
|
||||
module Listable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
validates :label, :position, presence: true, if: :label?
|
||||
validates :position, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :movable?
|
||||
|
||||
before_destroy :can_be_destroyed
|
||||
|
||||
scope :ordered, -> { order(:list_type, :position) }
|
||||
scope :destroyable, -> { where(list_type: list_types.slice(*destroyable_types).values) }
|
||||
scope :movable, -> { where(list_type: list_types.slice(*movable_types).values) }
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def destroyable_types
|
||||
[:label]
|
||||
end
|
||||
|
||||
def movable_types
|
||||
[:label]
|
||||
end
|
||||
end
|
||||
|
||||
def destroyable?
|
||||
self.class.destroyable_types.include?(list_type&.to_sym)
|
||||
end
|
||||
|
||||
def movable?
|
||||
self.class.movable_types.include?(list_type&.to_sym)
|
||||
end
|
||||
|
||||
def title
|
||||
label? ? label.name : list_type.humanize
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_be_destroyed
|
||||
throw(:abort) unless destroyable? # rubocop:disable Cop/BanCatchThrow
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class List < ApplicationRecord
|
||||
include Boards::Listable
|
||||
include Importable
|
||||
|
||||
belongs_to :board
|
||||
|
|
@ -10,30 +11,13 @@ class List < ApplicationRecord
|
|||
enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3, milestone: 4, iteration: 5 }
|
||||
|
||||
validates :board, :list_type, presence: true, unless: :importing?
|
||||
validates :label, :position, presence: true, if: :label?
|
||||
validates :label_id, uniqueness: { scope: :board_id }, if: :label?
|
||||
validates :position, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :movable?
|
||||
|
||||
before_destroy :can_be_destroyed
|
||||
|
||||
scope :destroyable, -> { where(list_type: list_types.slice(*destroyable_types).values) }
|
||||
scope :movable, -> { where(list_type: list_types.slice(*movable_types).values) }
|
||||
|
||||
scope :preload_associated_models, -> { preload(:board, label: :priorities) }
|
||||
|
||||
scope :ordered, -> { order(:list_type, :position) }
|
||||
|
||||
alias_method :preferences, :list_user_preferences
|
||||
|
||||
class << self
|
||||
def destroyable_types
|
||||
[:label]
|
||||
end
|
||||
|
||||
def movable_types
|
||||
[:label]
|
||||
end
|
||||
|
||||
def preload_preferences_for_user(lists, user)
|
||||
return unless user
|
||||
|
||||
|
|
@ -60,18 +44,6 @@ class List < ApplicationRecord
|
|||
preferences_for(user).update(preferences)
|
||||
end
|
||||
|
||||
def destroyable?
|
||||
self.class.destroyable_types.include?(list_type&.to_sym)
|
||||
end
|
||||
|
||||
def movable?
|
||||
self.class.movable_types.include?(list_type&.to_sym)
|
||||
end
|
||||
|
||||
def title
|
||||
label? ? label.name : list_type.humanize
|
||||
end
|
||||
|
||||
def collapsed?(user)
|
||||
preferences = preferences_for(user)
|
||||
|
||||
|
|
@ -95,12 +67,6 @@ class List < ApplicationRecord
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_be_destroyed
|
||||
throw(:abort) unless destroyable? # rubocop:disable Cop/BanCatchThrow
|
||||
end
|
||||
end
|
||||
|
||||
List.prepend_if_ee('::EE::List')
|
||||
|
|
|
|||
|
|
@ -20,33 +20,17 @@ module Packages
|
|||
end
|
||||
|
||||
def package_versions(packages = @packages)
|
||||
{ 'packages' => { packages.first.name => package_versions_map(packages) } }
|
||||
package_versions_index(packages).as_json
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def package_versions_map(packages)
|
||||
packages.each_with_object({}) do |package, map|
|
||||
map[package.version] = package_metadata(package)
|
||||
end
|
||||
def package_versions_sha(packages = @packages)
|
||||
package_versions_index(packages).sha
|
||||
end
|
||||
|
||||
def package_metadata(package)
|
||||
json = package.composer_metadatum.composer_json
|
||||
|
||||
json.merge('dist' => package_dist(package), 'uid' => package.id, 'version' => package.version)
|
||||
end
|
||||
|
||||
def package_dist(package)
|
||||
sha = package.composer_metadatum.target_sha
|
||||
archive_api_path = api_v4_projects_packages_composer_archives_package_name_path({ id: package.project_id, package_name: package.name, format: '.zip' }, true)
|
||||
|
||||
{
|
||||
'type' => 'zip',
|
||||
'url' => expose_url(archive_api_path) + "?sha=#{sha}",
|
||||
'reference' => sha,
|
||||
'shasum' => ''
|
||||
}
|
||||
def package_versions_index(packages)
|
||||
::Gitlab::Composer::VersionIndex.new(packages)
|
||||
end
|
||||
|
||||
def providers_map
|
||||
|
|
@ -59,10 +43,6 @@ module Packages
|
|||
map
|
||||
end
|
||||
|
||||
def package_versions_sha(packages)
|
||||
Digest::SHA256.hexdigest(package_versions(packages).to_json)
|
||||
end
|
||||
|
||||
def provider_sha
|
||||
Digest::SHA256.hexdigest(provider.to_json)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
- return unless Gitlab::Gitpod.feature_available?
|
||||
- expanded = integration_expanded?('gitpod_')
|
||||
|
||||
%section.settings.no-animate#js-gitpod-settings{ class: ('expanded' if expanded) }
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@
|
|||
.form-group
|
||||
= f.text_field :recaptcha_private_key, class: 'form-control'
|
||||
|
||||
.form-group
|
||||
.form-check
|
||||
= f.check_box :invisible_captcha_enabled, class: 'form-check-input'
|
||||
= f.label :invisible_captcha_enabled, class: 'form-check-label' do
|
||||
= _('Enable Invisible Captcha during sign up')
|
||||
%span.form-text.text-muted
|
||||
= _('Helps prevent bots from creating accounts.')
|
||||
|
||||
.form-group
|
||||
.form-check
|
||||
= f.check_box :akismet_enabled, class: 'form-check-input'
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
%p
|
||||
- recaptcha_v2_link_url = 'https://developers.google.com/recaptcha/docs/versions'
|
||||
- recaptcha_v2_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: recaptcha_v2_link_url }
|
||||
= _('Enable reCAPTCHA or Akismet and set IP limits. For reCAPTCHA, we currently only support %{recaptcha_v2_link_start}v2%{recaptcha_v2_link_end}').html_safe % { recaptcha_v2_link_start: recaptcha_v2_link_start, recaptcha_v2_link_end: '</a>'.html_safe }
|
||||
= _('Enable reCAPTCHA, Invisible Captcha, Akismet and set IP limits. For reCAPTCHA, we currently only support %{recaptcha_v2_link_start}v2%{recaptcha_v2_link_end}').html_safe % { recaptcha_v2_link_start: recaptcha_v2_link_start, recaptcha_v2_link_end: '</a>'.html_safe }
|
||||
.settings-content
|
||||
= render 'spam'
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
= form_for(resource, as: "new_#{resource_name}", url: url, html: { class: 'new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f|
|
||||
.devise-errors
|
||||
= render 'devise/shared/error_messages', resource: resource
|
||||
- if Feature.enabled?(:invisible_captcha)
|
||||
- if Gitlab::CurrentSettings.invisible_captcha_enabled
|
||||
= invisible_captcha
|
||||
.name.form-row
|
||||
.col.form-group
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
- page_title _('CI / CD Analytics')
|
||||
|
||||
- if Feature.enabled?(:graphql_pipeline_analytics)
|
||||
#js-project-pipelines-charts-app{ data: { project_path: @project.full_path } }
|
||||
#js-project-pipelines-charts-app{ data: { project_path: @project.full_path,
|
||||
should_render_deployment_frequency_charts: should_render_deployment_frequency_charts.to_s } }
|
||||
- else
|
||||
#js-project-pipelines-charts-app{ data: { counts: @counts, success_ratio: success_ratio(@counts),
|
||||
times_chart: { labels: @charts[:pipeline_times].labels, values: @charts[:pipeline_times].pipeline_times },
|
||||
last_week_chart: { labels: @charts[:week].labels, totals: @charts[:week].total, success: @charts[:week].success },
|
||||
last_month_chart: { labels: @charts[:month].labels, totals: @charts[:month].total, success: @charts[:month].success },
|
||||
last_year_chart: { labels: @charts[:year].labels, totals: @charts[:year].total, success: @charts[:year].success } } }
|
||||
last_year_chart: { labels: @charts[:year].labels, totals: @charts[:year].total, success: @charts[:year].success },
|
||||
project_path: @project.full_path,
|
||||
should_render_deployment_frequency_charts: should_render_deployment_frequency_charts.to_s } }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add configurable Gitpod button within projcet repository page
|
||||
merge_request: 51197
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add version cache field to composer metadata
|
||||
merge_request: 50906
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Show all quick actions in `/` autocomplete
|
||||
merge_request: 51239
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Control job status using exit codes
|
||||
merge_request: 51439
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update copy on Feature Flags List view to be more descriptive for users
|
||||
merge_request: 50813
|
||||
author: Sarah Rosenshine
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add setting to enable Invisible Captcha
|
||||
merge_request: 50650
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update the DAST latest template to run when configured even if the user doesn't have sufficient permission
|
||||
merge_request: 51279
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292024
|
|||
milestone: '13.7'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: gitpod
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37985
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258206
|
||||
milestone: '13.4'
|
||||
type: development
|
||||
group: group::editor
|
||||
default_enabled: true
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: invisible_captcha
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31625
|
||||
rollout_issue_url:
|
||||
milestone: '12.2'
|
||||
type: development
|
||||
group: group::acquisition
|
||||
default_enabled: false
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: widget_visibility_polling
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29318
|
||||
rollout_issue_url:
|
||||
milestone: '12.10'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: true
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddVersionShaCacheToComposerMetadata < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
add_column :packages_composer_metadata, :version_cache_sha, :binary, null: true
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_column :packages_composer_metadata, :version_cache_sha, :binary
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddInvisibleCaptchaEnabledToSettings < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
add_column :application_settings, :invisible_captcha_enabled, :boolean, null: false, default: false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
2278b1e4e19b5306e4b616eb87b622427ef2dcf73dae761739cb3106d5e64718
|
||||
|
|
@ -0,0 +1 @@
|
|||
cf391e617ef16f70c0daa4584959d36eda4b29c7e211f3f90ad74b4ebbc7ebbd
|
||||
|
|
@ -9388,6 +9388,7 @@ CREATE TABLE application_settings (
|
|||
disable_feed_token boolean DEFAULT false NOT NULL,
|
||||
personal_access_token_prefix text,
|
||||
rate_limiting_response_text text,
|
||||
invisible_captcha_enabled boolean DEFAULT false NOT NULL,
|
||||
container_registry_cleanup_tags_service_max_list_size integer DEFAULT 200 NOT NULL,
|
||||
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
|
||||
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
|
||||
|
|
@ -14711,7 +14712,8 @@ ALTER SEQUENCE packages_build_infos_id_seq OWNED BY packages_build_infos.id;
|
|||
CREATE TABLE packages_composer_metadata (
|
||||
package_id bigint NOT NULL,
|
||||
target_sha bytea NOT NULL,
|
||||
composer_json jsonb DEFAULT '{}'::jsonb NOT NULL
|
||||
composer_json jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
version_cache_sha bytea
|
||||
);
|
||||
|
||||
CREATE TABLE packages_conan_file_metadata (
|
||||
|
|
|
|||
|
|
@ -300,6 +300,7 @@ listed in the descriptions of the relevant settings.
|
|||
| `housekeeping_incremental_repack_period` | integer | required by: `housekeeping_enabled` | Number of Git pushes after which an incremental `git repack` is run. |
|
||||
| `html_emails_enabled` | boolean | no | Enable HTML emails. |
|
||||
| `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `bitbucket_server`, `gitlab`, `fogbugz`, `git`, `gitlab_project`, `gitea`, `manifest`, and `phabricator`. |
|
||||
| `invisible_captcha_enabled` | boolean | no | Enable Invisible Captcha spam detection during signup. Disabled by default. |
|
||||
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|
|
||||
| `local_markdown_version` | integer | no | Increase this value when any cached Markdown should be invalidated. |
|
||||
| `maintenance_mode_message` | string | no | **(PREMIUM)** Message displayed when instance is in maintenance mode |
|
||||
|
|
|
|||
|
|
@ -2278,10 +2278,10 @@ job3:
|
|||
#### `allow_failure:exit_codes`
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/273157) in GitLab 13.8.
|
||||
> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
|
||||
> - It's disabled on GitLab.com.
|
||||
> - It's not recommended for production use.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-allow_failureexit_codes). **(CORE ONLY)**
|
||||
> - It's [deployed behind a feature flag](../../user/feature_flags.md), enabled by default.
|
||||
> - It's enabled on GitLab.com.
|
||||
> - It's recommended for production use.
|
||||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-allow_failureexit_codes). **(CORE ONLY)**
|
||||
|
||||
WARNING:
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
|
@ -2310,16 +2310,10 @@ test_job_2:
|
|||
|
||||
##### Enable or disable `allow_failure:exit_codes` **(CORE ONLY)**
|
||||
|
||||
`allow_failure:exit_codes` is under development and not ready for production use. It is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
`allow_failure:exit_codes` is under development but ready for production use. It is
|
||||
deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
|
||||
can enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:ci_allow_failure_with_exit_codes)
|
||||
```
|
||||
can disable it.
|
||||
|
||||
To disable it:
|
||||
|
||||
|
|
@ -2327,6 +2321,12 @@ To disable it:
|
|||
Feature.disable(:ci_allow_failure_with_exit_codes)
|
||||
```
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:ci_allow_failure_with_exit_codes)
|
||||
```
|
||||
|
||||
### `when`
|
||||
|
||||
`when` is used to implement jobs that are run in case of failure or despite the
|
||||
|
|
|
|||
|
|
@ -8,14 +8,7 @@ info: "To determine the technical writer assigned to the Stage/Group associated
|
|||
# Gitpod Integration
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/228893) in GitLab 13.4.
|
||||
> - It was [deployed behind a feature flag](#enable-or-disable-the-gitpod-integration), disabled by default.
|
||||
> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/258206) in GitLab 13.5.
|
||||
> - It's enabled on GitLab.com.
|
||||
> - It's recommended for production use.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#configure-your-gitlab-instance-with-gitpod). **(CORE ONLY)**
|
||||
|
||||
WARNING:
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/258206) in GitLab 13.8
|
||||
|
||||
With [Gitpod](https://gitpod.io/) you can describe your dev environment as code to get fully set
|
||||
up, compiled, and tested dev environments for any GitLab project. The dev environments are not only
|
||||
|
|
@ -48,28 +41,14 @@ can follow the same steps once the integration has been enabled and configured b
|
|||
|
||||
## Configure your GitLab instance with Gitpod **(CORE ONLY)**
|
||||
|
||||
If you are new to Gitpod, head over to the [Gitpod documentation](https://www.gitpod.io/docs/self-hosted/latest/self-hosted/)
|
||||
and get your instance up and running.
|
||||
The integration of Gitpod with GitLab is enabled on GitLab.com and available to all users.
|
||||
For GitLab self-managed instances, a GitLab administrator needs to enable it through the admin settings.
|
||||
|
||||
First, you (GitLab admin) need to set up a Gitpod instance to integrate with GitLab.
|
||||
Head over to the [Gitpod documentation](https://www.gitpod.io/docs/self-hosted/latest/self-hosted/) to
|
||||
get your instance up and running. Once done:
|
||||
|
||||
1. In GitLab, go to **Admin Area > Settings > General**.
|
||||
1. Expand the **Gitpod** configuration section.
|
||||
1. Check **Enable Gitpod**.
|
||||
1. Add your Gitpod instance URL (for example, `https://gitpod.example.com`).
|
||||
|
||||
## Enable or disable the Gitpod integration **(CORE ONLY)**
|
||||
|
||||
The Gitpod integration is deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
|
||||
can enable or disable it.
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:gitpod)
|
||||
```
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:gitpod)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
> - Introduced in [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/26672):
|
||||
> once an action is executed, an alert appears when a quick action is successfully applied.
|
||||
> - In [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/16877) and later, you can use
|
||||
> - Introduced in [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/16877): you can use
|
||||
> quick actions when updating the description of issues, epics, and merge requests.
|
||||
> - Introduced in [GitLab 13.8](https://gitlab.com/gitlab-org/gitlab/-/issues/292393): when you enter
|
||||
> `/` into a description or comment field, all available quick actions are displayed in a scrollable list.
|
||||
|
||||
Quick actions are textual shortcuts for common actions on issues, epics, merge requests,
|
||||
and commits that are usually done by clicking buttons or dropdowns in the GitLab UI.
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ module API
|
|||
optional :import_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
|
||||
values: %w[github bitbucket bitbucket_server gitlab google_code fogbugz git gitlab_project gitea manifest phabricator],
|
||||
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
|
||||
optional :invisible_captcha_enabled, type: Boolean, desc: 'Enable Invisible Captcha spam detection during signup.'
|
||||
optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size for each job's artifacts"
|
||||
optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB'
|
||||
optional :max_import_size, type: Integer, desc: 'Maximum import size in MB'
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def self.allow_failure_with_exit_codes_enabled?
|
||||
::Feature.enabled?(:ci_allow_failure_with_exit_codes)
|
||||
::Feature.enabled?(:ci_allow_failure_with_exit_codes, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def self.rules_variables_enabled?(project)
|
||||
|
|
|
|||
|
|
@ -38,8 +38,6 @@ dast:
|
|||
$CI_KUBERNETES_ACTIVE &&
|
||||
$GITLAB_FEATURES =~ /\bdast\b/
|
||||
- if: $CI_COMMIT_BRANCH &&
|
||||
$GITLAB_FEATURES =~ /\bdast\b/ &&
|
||||
$DAST_WEBSITE
|
||||
- if: $CI_COMMIT_BRANCH &&
|
||||
$GITLAB_FEATURES =~ /\bdast\b/ &&
|
||||
$DAST_API_SPECIFICATION
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Composer
|
||||
class VersionIndex
|
||||
include API::Helpers::RelatedResourcesHelpers
|
||||
|
||||
def initialize(packages)
|
||||
@packages = packages
|
||||
end
|
||||
|
||||
def as_json(_options = nil)
|
||||
{ 'packages' => { @packages.first.name => package_versions_map } }
|
||||
end
|
||||
|
||||
def sha
|
||||
Digest::SHA256.hexdigest(to_json)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def package_versions_map
|
||||
@packages.each_with_object({}) do |package, map|
|
||||
map[package.version] = package_metadata(package)
|
||||
end
|
||||
end
|
||||
|
||||
def package_metadata(package)
|
||||
json = package.composer_metadatum.composer_json
|
||||
|
||||
json.merge('dist' => package_dist(package), 'uid' => package.id, 'version' => package.version)
|
||||
end
|
||||
|
||||
def package_dist(package)
|
||||
sha = package.composer_metadatum.target_sha
|
||||
archive_api_path = api_v4_projects_packages_composer_archives_package_name_path({ id: package.project_id, package_name: package.name, format: '.zip' }, true)
|
||||
|
||||
{
|
||||
'type' => 'zip',
|
||||
'url' => expose_url(archive_api_path) + "?sha=#{sha}",
|
||||
'reference' => sha,
|
||||
'shasum' => ''
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
class Gitpod
|
||||
class << self
|
||||
def feature_available?
|
||||
# The gitpod_bundle feature could be conditionally applied, so check if `!off?`
|
||||
!feature.off? || feature_enabled?
|
||||
end
|
||||
|
||||
def feature_enabled?(actor = nil)
|
||||
Feature.enabled?(:gitpod, actor, default_enabled: true)
|
||||
end
|
||||
|
||||
def feature_and_settings_enabled?(actor = nil)
|
||||
feature_enabled?(actor) && Gitlab::CurrentSettings.gitpod_enabled
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def feature
|
||||
Feature.get(:gitpod) # rubocop:disable Gitlab/AvoidFeatureGet
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -8827,6 +8827,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Do you want to discard your changes?"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Edit profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Edit scanner profile"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8914,6 +8917,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Scanner Profiles"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Scanner name"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Show debug messages"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8923,6 +8929,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Site Profiles"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Site name"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Spider timeout"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8941,6 +8950,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Turn on AJAX spider"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Username"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8950,6 +8962,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Validated"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validation status"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastSiteValidation|Copy HTTP header to clipboard"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8962,6 +8977,9 @@ msgstr ""
|
|||
msgid "DastSiteValidation|Header validation"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastSiteValidation|Retry validation"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastSiteValidation|Step 1 - Choose site validation method"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -9604,6 +9622,27 @@ msgstr ""
|
|||
msgid "Deployment Frequency"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeploymentFrequencyCharts|Date"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeploymentFrequencyCharts|Deployments"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeploymentFrequencyCharts|Deployments charts"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeploymentFrequencyCharts|Deployments to production for last month (%{startDate} - %{endDate})"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeploymentFrequencyCharts|Deployments to production for last week (%{startDate} - %{endDate})"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeploymentFrequencyCharts|Deployments to production for the last 90 days (%{startDate} - %{endDate})"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeploymentFrequencyCharts|Something went wrong while getting deployment frequency data"
|
||||
msgstr ""
|
||||
|
||||
msgid "Deployment|API"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -10605,6 +10644,9 @@ msgstr ""
|
|||
msgid "Enable Incident Management inbound alert limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable Invisible Captcha during sign up"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable Kroki"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -10692,7 +10734,7 @@ msgstr ""
|
|||
msgid "Enable proxy"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable reCAPTCHA or Akismet and set IP limits. For reCAPTCHA, we currently only support %{recaptcha_v2_link_start}v2%{recaptcha_v2_link_end}"
|
||||
msgid "Enable reCAPTCHA, Invisible Captcha, Akismet and set IP limits. For reCAPTCHA, we currently only support %{recaptcha_v2_link_start}v2%{recaptcha_v2_link_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable shared runners"
|
||||
|
|
@ -30677,7 +30719,7 @@ msgstr ""
|
|||
msgid "UserLists|Enter a comma separated list of user IDs. These IDs should be the users of the system in which the feature flag is set, not GitLab IDs"
|
||||
msgstr ""
|
||||
|
||||
msgid "UserLists|Feature flag list"
|
||||
msgid "UserLists|Feature flag user list"
|
||||
msgstr ""
|
||||
|
||||
msgid "UserLists|Lists allow you to define a set of users to be used with feature flags. %{linkStart}Read more about feature flag lists.%{linkEnd}"
|
||||
|
|
@ -33849,6 +33891,9 @@ msgstr ""
|
|||
msgid "mrWidget|to start a merge train when the pipeline succeeds"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be a boolean value"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be a root namespace"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "1.178.0",
|
||||
"@gitlab/tributejs": "1.0.0",
|
||||
"@gitlab/ui": "25.7.3",
|
||||
"@gitlab/ui": "25.8.0",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-4",
|
||||
"@rails/ujs": "^6.0.3-4",
|
||||
|
|
|
|||
|
|
@ -14,14 +14,6 @@ module QA
|
|||
sign_up.fill_new_user_username_field(user.username)
|
||||
sign_up.fill_new_user_email_field(user.email)
|
||||
sign_up.fill_new_user_password_field(user.password)
|
||||
|
||||
# Because invisible_captcha would prevent submitting this form
|
||||
# within 4 seconds, sleep here. This can be removed once we
|
||||
# implement invisible_captcha as an application setting instead
|
||||
# of a feature flag, so we can turn it off while testing.
|
||||
# Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/284113
|
||||
sleep 5
|
||||
|
||||
sign_up.click_new_user_register_button
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ RSpec.describe RegistrationsController do
|
|||
include TermsHelper
|
||||
|
||||
before do
|
||||
stub_feature_flags(invisible_captcha: false)
|
||||
stub_application_setting(require_admin_approval_after_user_signup: false)
|
||||
end
|
||||
|
||||
|
|
@ -193,15 +192,10 @@ RSpec.describe RegistrationsController do
|
|||
|
||||
context 'when invisible captcha is enabled' do
|
||||
before do
|
||||
stub_feature_flags(invisible_captcha: true)
|
||||
InvisibleCaptcha.timestamp_enabled = true
|
||||
stub_application_setting(invisible_captcha_enabled: true)
|
||||
InvisibleCaptcha.timestamp_threshold = treshold
|
||||
end
|
||||
|
||||
after do
|
||||
InvisibleCaptcha.timestamp_enabled = false
|
||||
end
|
||||
|
||||
let(:treshold) { 4 }
|
||||
let(:session_params) { { invisible_captcha_timestamp: form_rendered_time.iso8601 } }
|
||||
let(:form_rendered_time) { Time.current }
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@ RSpec.describe 'Admin updates settings' do
|
|||
end
|
||||
|
||||
context 'General page' do
|
||||
let(:gitpod_feature_enabled) { true }
|
||||
|
||||
before do
|
||||
stub_feature_flags(gitpod: gitpod_feature_enabled)
|
||||
visit general_admin_application_settings_path
|
||||
end
|
||||
|
||||
|
|
@ -224,28 +221,16 @@ RSpec.describe 'Admin updates settings' do
|
|||
end
|
||||
|
||||
context 'Configure Gitpod' do
|
||||
context 'with feature disabled' do
|
||||
let(:gitpod_feature_enabled) { false }
|
||||
|
||||
it 'do not show settings' do
|
||||
expect(page).not_to have_selector('#js-gitpod-settings')
|
||||
it 'changes gitpod settings' do
|
||||
page.within('#js-gitpod-settings') do
|
||||
check 'Enable Gitpod integration'
|
||||
fill_in 'Gitpod URL', with: 'https://gitpod.test/'
|
||||
click_button 'Save changes'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with feature enabled' do
|
||||
let(:gitpod_feature_enabled) { true }
|
||||
|
||||
it 'changes gitpod settings' do
|
||||
page.within('#js-gitpod-settings') do
|
||||
check 'Enable Gitpod integration'
|
||||
fill_in 'Gitpod URL', with: 'https://gitpod.test/'
|
||||
click_button 'Save changes'
|
||||
end
|
||||
|
||||
expect(page).to have_content 'Application settings saved successfully'
|
||||
expect(current_settings.gitpod_url).to eq('https://gitpod.test/')
|
||||
expect(current_settings.gitpod_enabled).to be(true)
|
||||
end
|
||||
expect(page).to have_content 'Application settings saved successfully'
|
||||
expect(current_settings.gitpod_url).to eq('https://gitpod.test/')
|
||||
expect(current_settings.gitpod_enabled).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -294,6 +294,15 @@ RSpec.describe 'GFM autocomplete', :js do
|
|||
user_item = find('.atwho-view li', text: user.username)
|
||||
expect(user_item).to have_content(user.username)
|
||||
end
|
||||
|
||||
it 'does not limit quick actions autocomplete list to 5' do
|
||||
note = find('#note-body')
|
||||
page.within '.timeline-content-form' do
|
||||
note.native.send_keys('/')
|
||||
end
|
||||
|
||||
expect(page).to have_selector('.atwho-view li', minimum: 6, visible: true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'assignees' do
|
||||
|
|
|
|||
|
|
@ -294,17 +294,13 @@ RSpec.describe 'Signup' do
|
|||
|
||||
context 'when reCAPTCHA and invisible captcha are enabled' do
|
||||
before do
|
||||
InvisibleCaptcha.timestamp_enabled = true
|
||||
stub_application_setting(invisible_captcha_enabled: true)
|
||||
stub_application_setting(recaptcha_enabled: true)
|
||||
allow_next_instance_of(RegistrationsController) do |instance|
|
||||
allow(instance).to receive(:verify_recaptcha).and_return(true)
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
InvisibleCaptcha.timestamp_enabled = false
|
||||
end
|
||||
|
||||
context 'when reCAPTCHA detects malicious behaviour' do
|
||||
before do
|
||||
allow_next_instance_of(RegistrationsController) do |instance|
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import EditBlob from '~/blob_edit/edit_blob';
|
||||
import EditorLite from '~/editor/editor_lite';
|
||||
import { EditorMarkdownExtension } from '~/editor/editor_markdown_ext';
|
||||
import { FileTemplateExtension } from '~/editor/editor_file_template_ext';
|
||||
import { EditorMarkdownExtension } from '~/editor/extensions/editor_markdown_ext';
|
||||
import { FileTemplateExtension } from '~/editor/extensions/editor_file_template_ext';
|
||||
|
||||
jest.mock('~/editor/editor_lite');
|
||||
jest.mock('~/editor/editor_markdown_ext');
|
||||
jest.mock('~/editor/editor_file_template_ext');
|
||||
jest.mock('~/editor/extensions/editor_markdown_ext');
|
||||
jest.mock('~/editor/extensions/editor_file_template_ext');
|
||||
|
||||
describe('Blob Editing', () => {
|
||||
const useMock = jest.fn();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { languages } from 'monaco-editor';
|
||||
import EditorLite from '~/editor/editor_lite';
|
||||
import { CiSchemaExtension } from '~/editor/editor_ci_schema_ext';
|
||||
import { CiSchemaExtension } from '~/editor/extensions/editor_ci_schema_ext';
|
||||
import { EXTENSION_CI_SCHEMA_FILE_NAME_MATCH } from '~/editor/constants';
|
||||
|
||||
describe('~/editor/editor_ci_config_ext', () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ERROR_INSTANCE_REQUIRED_FOR_EXTENSION } from '~/editor/constants';
|
||||
import { EditorLiteExtension } from '~/editor/editor_lite_extension_base';
|
||||
import { EditorLiteExtension } from '~/editor/extensions/editor_lite_extension_base';
|
||||
|
||||
describe('The basis for an Editor Lite extension', () => {
|
||||
let ext;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { editor as monacoEditor, languages as monacoLanguages, Uri } from 'monaco-editor';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import Editor from '~/editor/editor_lite';
|
||||
import { EditorLiteExtension } from '~/editor/editor_lite_extension_base';
|
||||
import { EditorLiteExtension } from '~/editor/extensions/editor_lite_extension_base';
|
||||
import { DEFAULT_THEME, themes } from '~/ide/lib/themes';
|
||||
import { EDITOR_LITE_INSTANCE_ERROR_NO_EL, URI_PREFIX } from '~/editor/constants';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Range, Position } from 'monaco-editor';
|
||||
import EditorLite from '~/editor/editor_lite';
|
||||
import { EditorMarkdownExtension } from '~/editor/editor_markdown_ext';
|
||||
import { EditorMarkdownExtension } from '~/editor/extensions/editor_markdown_ext';
|
||||
|
||||
describe('Markdown Extension for Editor Lite', () => {
|
||||
let editor;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`PipelinesAreaChart matches the snapshot 1`] = `
|
||||
exports[`CiCdAnalyticsAreaChart matches the snapshot 1`] = `
|
||||
<div
|
||||
class="gl-mt-3"
|
||||
>
|
||||
|
|
@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
|
|||
import { GlColumnChart } from '@gitlab/ui/dist/charts';
|
||||
import Component from '~/projects/pipelines/charts/components/app_legacy.vue';
|
||||
import StatisticsList from '~/projects/pipelines/charts/components/statistics_list.vue';
|
||||
import PipelinesAreaChart from '~/projects/pipelines/charts/components/pipelines_area_chart.vue';
|
||||
import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue';
|
||||
import {
|
||||
counts,
|
||||
timesChartData,
|
||||
|
|
@ -23,6 +23,13 @@ describe('ProjectsPipelinesChartsApp', () => {
|
|||
lastMonthChartData,
|
||||
lastYearChartData,
|
||||
},
|
||||
provide: {
|
||||
projectPath: 'test/project',
|
||||
shouldRenderDeploymentFrequencyCharts: true,
|
||||
},
|
||||
stubs: {
|
||||
DeploymentFrequencyCharts: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -52,12 +59,12 @@ describe('ProjectsPipelinesChartsApp', () => {
|
|||
|
||||
describe('pipelines charts', () => {
|
||||
it('displays 3 area charts', () => {
|
||||
expect(wrapper.findAll(PipelinesAreaChart).length).toBe(3);
|
||||
expect(wrapper.findAll(CiCdAnalyticsAreaChart).length).toBe(3);
|
||||
});
|
||||
|
||||
describe('displays individual correctly', () => {
|
||||
it('renders with the correct data', () => {
|
||||
const charts = wrapper.findAll(PipelinesAreaChart);
|
||||
const charts = wrapper.findAll(CiCdAnalyticsAreaChart);
|
||||
|
||||
for (let i = 0; i < charts.length; i += 1) {
|
||||
const chart = charts.at(i);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { merge } from 'lodash';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createMockApollo from 'jest/helpers/mock_apollo_helper';
|
||||
import { GlColumnChart } from '@gitlab/ui/dist/charts';
|
||||
import Component from '~/projects/pipelines/charts/components/app.vue';
|
||||
import StatisticsList from '~/projects/pipelines/charts/components/statistics_list.vue';
|
||||
import PipelinesAreaChart from '~/projects/pipelines/charts/components/pipelines_area_chart.vue';
|
||||
import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue';
|
||||
import getPipelineCountByStatus from '~/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql';
|
||||
import getProjectPipelineStatistics from '~/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql';
|
||||
import { mockPipelineCount, mockPipelineStatistics } from '../mock_data';
|
||||
|
|
@ -13,6 +14,8 @@ const projectPath = 'gitlab-org/gitlab';
|
|||
const localVue = createLocalVue();
|
||||
localVue.use(VueApollo);
|
||||
|
||||
const DeploymentFrequencyChartsStub = { name: 'DeploymentFrequencyCharts', render: () => {} };
|
||||
|
||||
describe('ProjectsPipelinesChartsApp', () => {
|
||||
let wrapper;
|
||||
|
||||
|
|
@ -25,21 +28,29 @@ describe('ProjectsPipelinesChartsApp', () => {
|
|||
return createMockApollo(requestHandlers);
|
||||
}
|
||||
|
||||
function createComponent(options = {}) {
|
||||
const { fakeApollo } = options;
|
||||
|
||||
return shallowMount(Component, {
|
||||
provide: {
|
||||
projectPath,
|
||||
},
|
||||
localVue,
|
||||
apolloProvider: fakeApollo,
|
||||
});
|
||||
function createComponent(mountOptions = {}) {
|
||||
wrapper = shallowMount(
|
||||
Component,
|
||||
merge(
|
||||
{},
|
||||
{
|
||||
provide: {
|
||||
projectPath,
|
||||
shouldRenderDeploymentFrequencyCharts: false,
|
||||
},
|
||||
localVue,
|
||||
apolloProvider: createMockApolloProvider(),
|
||||
stubs: {
|
||||
DeploymentFrequencyCharts: DeploymentFrequencyChartsStub,
|
||||
},
|
||||
},
|
||||
mountOptions,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
const fakeApollo = createMockApolloProvider();
|
||||
wrapper = createComponent({ fakeApollo });
|
||||
createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -73,12 +84,12 @@ describe('ProjectsPipelinesChartsApp', () => {
|
|||
|
||||
describe('pipelines charts', () => {
|
||||
it('displays 3 area charts', () => {
|
||||
expect(wrapper.findAll(PipelinesAreaChart)).toHaveLength(3);
|
||||
expect(wrapper.findAll(CiCdAnalyticsAreaChart)).toHaveLength(3);
|
||||
});
|
||||
|
||||
describe('displays individual correctly', () => {
|
||||
it('renders with the correct data', () => {
|
||||
const charts = wrapper.findAll(PipelinesAreaChart);
|
||||
const charts = wrapper.findAll(CiCdAnalyticsAreaChart);
|
||||
|
||||
for (let i = 0; i < charts.length; i += 1) {
|
||||
const chart = charts.at(i);
|
||||
|
|
@ -92,4 +103,26 @@ describe('ProjectsPipelinesChartsApp', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
const findDeploymentFrequencyCharts = () => wrapper.find(DeploymentFrequencyChartsStub);
|
||||
|
||||
describe('when shouldRenderDeploymentFrequencyCharts is true', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ provide: { shouldRenderDeploymentFrequencyCharts: true } });
|
||||
});
|
||||
|
||||
it('renders the deployment frequency charts', () => {
|
||||
expect(findDeploymentFrequencyCharts().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when shouldRenderDeploymentFrequencyCharts is false', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ provide: { shouldRenderDeploymentFrequencyCharts: false } });
|
||||
});
|
||||
|
||||
it('does not render the deployment frequency charts', () => {
|
||||
expect(findDeploymentFrequencyCharts().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,23 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import Component from '~/projects/pipelines/charts/components/pipelines_area_chart.vue';
|
||||
import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue';
|
||||
import { transformedAreaChartData } from '../mock_data';
|
||||
|
||||
describe('PipelinesAreaChart', () => {
|
||||
describe('CiCdAnalyticsAreaChart', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(Component, {
|
||||
wrapper = mount(CiCdAnalyticsAreaChart, {
|
||||
propsData: {
|
||||
chartData: transformedAreaChartData,
|
||||
areaChartOptions: {
|
||||
xAxis: {
|
||||
name: 'X axis title',
|
||||
type: 'category',
|
||||
},
|
||||
yAxis: {
|
||||
name: 'Y axis title',
|
||||
},
|
||||
},
|
||||
},
|
||||
slots: {
|
||||
default: 'Some title',
|
||||
|
|
@ -15,4 +15,16 @@ RSpec.describe GraphHelper do
|
|||
expect(refs).to match('master')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#should_render_deployment_frequency_charts' do
|
||||
let(:project) { create(:project, :private) }
|
||||
|
||||
before do
|
||||
self.instance_variable_set(:@project, project)
|
||||
end
|
||||
|
||||
it 'always returns false' do
|
||||
expect(should_render_deployment_frequency_charts).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -330,9 +330,8 @@ RSpec.describe TreeHelper do
|
|||
end
|
||||
end
|
||||
|
||||
context 'gitpod feature is enabled' do
|
||||
context 'gitpod settings is enabled' do
|
||||
before do
|
||||
stub_feature_flags(gitpod: true)
|
||||
allow(Gitlab::CurrentSettings)
|
||||
.to receive(:gitpod_enabled)
|
||||
.and_return(true)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Composer::VersionIndex do
|
||||
let_it_be(:package_name) { 'sample-project' }
|
||||
let_it_be(:json) { { 'name' => package_name } }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, :custom_repo, files: { 'composer.json' => json.to_json }, group: group) }
|
||||
let_it_be(:package1) { create(:composer_package, :with_metadatum, project: project, name: package_name, version: '1.0.0', json: json) }
|
||||
let_it_be(:package2) { create(:composer_package, :with_metadatum, project: project, name: package_name, version: '2.0.0', json: json) }
|
||||
|
||||
let(:branch) { project.repository.find_branch('master') }
|
||||
|
||||
let(:packages) { [package1, package2] }
|
||||
|
||||
describe '#as_json' do
|
||||
subject(:index) { described_class.new(packages).as_json }
|
||||
|
||||
def expected_json(package)
|
||||
{
|
||||
'dist' => {
|
||||
'reference' => branch.target,
|
||||
'shasum' => '',
|
||||
'type' => 'zip',
|
||||
'url' => "http://localhost/api/v4/projects/#{project.id}/packages/composer/archives/#{package.name}.zip?sha=#{branch.target}"
|
||||
},
|
||||
'name' => package.name,
|
||||
'uid' => package.id,
|
||||
'version' => package.version
|
||||
}
|
||||
end
|
||||
|
||||
it 'returns the packages json' do
|
||||
packages = index['packages'][package_name]
|
||||
|
||||
expect(packages['1.0.0']).to eq(expected_json(package1))
|
||||
expect(packages['2.0.0']).to eq(expected_json(package2))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#sha' do
|
||||
subject(:sha) { described_class.new(packages).sha }
|
||||
|
||||
it 'returns the json SHA' do
|
||||
expect(sha).to match /^[A-Fa-f0-9]{64}$/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Gitpod do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(gitpod: feature_scope)
|
||||
end
|
||||
|
||||
describe '.feature_available?' do
|
||||
subject { described_class.feature_available? }
|
||||
|
||||
context 'when feature has not been set' do
|
||||
let(:feature_scope) { nil }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when feature is disabled' do
|
||||
let(:feature_scope) { false }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'when feature is enabled globally' do
|
||||
let(:feature_scope) { true }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when feature is enabled only to a resource' do
|
||||
let(:feature_scope) { user }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.feature_enabled?' do
|
||||
let(:current_user) { nil }
|
||||
|
||||
subject { described_class.feature_enabled?(current_user) }
|
||||
|
||||
context 'when feature has not been set' do
|
||||
let(:feature_scope) { nil }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when feature is enabled globally' do
|
||||
let(:feature_scope) { true }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when feature is enabled only to a resource' do
|
||||
let(:feature_scope) { user }
|
||||
|
||||
context 'for the same resource' do
|
||||
let(:current_user) { user }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'for a different resource' do
|
||||
let(:current_user) { create(:user) }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -4,6 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe List do
|
||||
it_behaves_like 'having unique enum values'
|
||||
it_behaves_like 'boards listable model', :list
|
||||
|
||||
describe 'relationships' do
|
||||
it { is_expected.to belong_to(:board) }
|
||||
|
|
@ -14,72 +15,6 @@ RSpec.describe List do
|
|||
it { is_expected.to validate_presence_of(:board) }
|
||||
it { is_expected.to validate_presence_of(:label) }
|
||||
it { is_expected.to validate_presence_of(:list_type) }
|
||||
it { is_expected.to validate_presence_of(:position) }
|
||||
it { is_expected.to validate_numericality_of(:position).only_integer.is_greater_than_or_equal_to(0) }
|
||||
|
||||
context 'when list_type is set to closed' do
|
||||
subject { described_class.new(list_type: :closed) }
|
||||
|
||||
it { is_expected.not_to validate_presence_of(:label) }
|
||||
it { is_expected.not_to validate_presence_of(:position) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy' do
|
||||
it 'can be destroyed when list_type is set to label' do
|
||||
subject = create(:list)
|
||||
|
||||
expect(subject.destroy).to be_truthy
|
||||
end
|
||||
|
||||
it 'can not be destroyed when list_type is set to closed' do
|
||||
subject = create(:closed_list)
|
||||
|
||||
expect(subject.destroy).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroyable?' do
|
||||
it 'returns true when list_type is set to label' do
|
||||
subject.list_type = :label
|
||||
|
||||
expect(subject).to be_destroyable
|
||||
end
|
||||
|
||||
it 'returns false when list_type is set to closed' do
|
||||
subject.list_type = :closed
|
||||
|
||||
expect(subject).not_to be_destroyable
|
||||
end
|
||||
end
|
||||
|
||||
describe '#movable?' do
|
||||
it 'returns true when list_type is set to label' do
|
||||
subject.list_type = :label
|
||||
|
||||
expect(subject).to be_movable
|
||||
end
|
||||
|
||||
it 'returns false when list_type is set to closed' do
|
||||
subject.list_type = :closed
|
||||
|
||||
expect(subject).not_to be_movable
|
||||
end
|
||||
end
|
||||
|
||||
describe '#title' do
|
||||
it 'returns label name when list_type is set to label' do
|
||||
subject.list_type = :label
|
||||
subject.label = Label.new(name: 'Development')
|
||||
|
||||
expect(subject.title).to eq 'Development'
|
||||
end
|
||||
|
||||
it 'returns Closed when list_type is set to closed' do
|
||||
subject.list_type = :closed
|
||||
|
||||
expect(subject.title).to eq 'Closed'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_preferences_for' do
|
||||
|
|
|
|||
|
|
@ -369,8 +369,5 @@ end
|
|||
# Prevent Rugged from picking up local developer gitconfig.
|
||||
Rugged::Settings['search_path_global'] = Rails.root.join('tmp/tests').to_s
|
||||
|
||||
# Disable timestamp checks for invisible_captcha
|
||||
InvisibleCaptcha.timestamp_enabled = false
|
||||
|
||||
# Initialize FactoryDefault to use create_default helper
|
||||
TestProf::FactoryDefault.init
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'boards listable model' do |list_factory|
|
||||
subject { build(list_factory) }
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to validate_presence_of(:position) }
|
||||
it { is_expected.to validate_numericality_of(:position).only_integer.is_greater_than_or_equal_to(0) }
|
||||
|
||||
context 'when list_type is set to closed' do
|
||||
subject { build(list_factory, list_type: :closed) }
|
||||
|
||||
it { is_expected.not_to validate_presence_of(:label) }
|
||||
it { is_expected.not_to validate_presence_of(:position) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'scopes' do
|
||||
describe '.ordered' do
|
||||
it 'returns lists ordered by type and position' do
|
||||
# rubocop:disable Rails/SaveBang
|
||||
lists = [
|
||||
create(list_factory, list_type: :backlog),
|
||||
create(list_factory, list_type: :closed),
|
||||
create(list_factory, position: 1),
|
||||
create(list_factory, position: 2)
|
||||
]
|
||||
# rubocop:enable Rails/SaveBang
|
||||
|
||||
expect(described_class.where(id: lists).ordered).to eq([lists[0], lists[2], lists[3], lists[1]])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroyable?' do
|
||||
it 'returns true when list_type is set to label' do
|
||||
subject.list_type = :label
|
||||
|
||||
expect(subject).to be_destroyable
|
||||
end
|
||||
|
||||
it 'returns false when list_type is set to closed' do
|
||||
subject.list_type = :closed
|
||||
|
||||
expect(subject).not_to be_destroyable
|
||||
end
|
||||
end
|
||||
|
||||
describe '#movable?' do
|
||||
it 'returns true when list_type is set to label' do
|
||||
subject.list_type = :label
|
||||
|
||||
expect(subject).to be_movable
|
||||
end
|
||||
|
||||
it 'returns false when list_type is set to closed' do
|
||||
subject.list_type = :closed
|
||||
|
||||
expect(subject).not_to be_movable
|
||||
end
|
||||
end
|
||||
|
||||
describe '#title' do
|
||||
it 'returns label name when list_type is set to label' do
|
||||
subject.list_type = :label
|
||||
subject.label = Label.new(name: 'Development')
|
||||
|
||||
expect(subject.title).to eq 'Development'
|
||||
end
|
||||
|
||||
it 'returns Closed when list_type is set to closed' do
|
||||
subject.list_type = :closed
|
||||
|
||||
expect(subject.title).to eq 'Closed'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy' do
|
||||
it 'can be destroyed when list_type is set to label' do
|
||||
subject = create(list_factory) # rubocop:disable Rails/SaveBang
|
||||
|
||||
expect(subject.destroy).to be_truthy
|
||||
end
|
||||
|
||||
it 'can not be destroyed when list_type is set to closed' do
|
||||
subject = create(list_factory, list_type: :closed) # rubocop:disable Rails/SaveBang
|
||||
|
||||
expect(subject.destroy).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -876,10 +876,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
|
||||
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
|
||||
|
||||
"@gitlab/ui@25.7.3":
|
||||
version "25.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-25.7.3.tgz#abc5caa585c307f38e01a7f061d9da0bc238acd0"
|
||||
integrity sha512-Ia+Pqmy4tQ5oztxXSjViM6YMt13MGyrQ6dqOn3zsiKG9RLh9kSBMtCif9jA0dDT17P+HMIdBYxYc6rTRjbfC0w==
|
||||
"@gitlab/ui@25.8.0":
|
||||
version "25.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-25.8.0.tgz#038090bc56215d2b0e5526097e1a16b0089ba5f4"
|
||||
integrity sha512-h84StVkrviIm1cMDmGb2+Q8R+U6wCjddz7IXKpgkTNitxYzAcwPSIp7cS1FkZ6eWEG9dVeB6uj7JpUhGqAzvfw==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue