...
 
Commits (89)
......@@ -8,7 +8,7 @@ require:
- rubocop-rspec
AllCops:
TargetRubyVersion: 2.5
TargetRubyVersion: 2.6
TargetRailsVersion: 5.0
Exclude:
- 'vendor/**/*'
......
......@@ -54,6 +54,7 @@ gem 'gssapi', group: :kerberos
# Spam and anti-bot protection
gem 'recaptcha', '~> 4.11', require: 'recaptcha/rails'
gem 'akismet', '~> 2.0'
gem 'invisible_captcha', '~> 0.12.1'
# Two-factor authentication
gem 'devise-two-factor', '~> 3.0.0'
......
......@@ -465,6 +465,8 @@ GEM
influxdb (0.2.3)
cause
json
invisible_captcha (0.12.1)
rails (>= 3.2.0)
ipaddress (0.8.3)
jaeger-client (0.10.0)
opentracing (~> 0.3)
......@@ -1166,6 +1168,7 @@ DEPENDENCIES
httparty (~> 0.16.4)
icalendar
influxdb (~> 0.2)
invisible_captcha (~> 0.12.1)
jira-ruby (~> 1.4)
js_regex (~> 3.1)
json-schema (~> 2.8.0)
......
......@@ -107,6 +107,7 @@ function deferredInitialisation() {
.then(() => {
$('select.select2').select2({
width: 'resolve',
minimumResultsForSearch: 10,
dropdownAutoWidth: true,
});
......
<script>
import { __ } from '~/locale';
import { mapState } from 'vuex';
import { GlLink, GlButton } from '@gitlab/ui';
import { GlLink } from '@gitlab/ui';
import { GlAreaChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
import dateFormat from 'dateformat';
import { debounceByAnimationFrame, roundOffFloat } from '~/lib/utils/common_utils';
......@@ -16,7 +15,6 @@ let debouncedResize;
export default {
components: {
GlAreaChart,
GlButton,
GlChartSeriesLabel,
GlLink,
Icon,
......@@ -69,7 +67,6 @@ export default {
};
},
computed: {
...mapState('monitoringDashboard', ['exportMetricsToCsvEnabled']),
chartData() {
// Transforms & supplements query data to render appropriate labels & styles
// Input: [{ queryAttributes1 }, { queryAttributes2 }]
......@@ -179,18 +176,6 @@ export default {
yAxisLabel() {
return `${this.graphData.y_label}`;
},
csvText() {
const chartData = this.chartData[0].data;
const header = `timestamp,${this.graphData.y_label}\r\n`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings
return chartData.reduce((csv, data) => {
const row = data.join(',');
return `${csv}${row}\r\n`;
}, header);
},
downloadLink() {
const data = new Blob([this.csvText], { type: 'text/plain' });
return window.URL.createObjectURL(data);
},
},
watch: {
containerWidth: 'onResize',
......@@ -259,16 +244,6 @@ export default {
<div :class="{ 'prometheus-graph-embed w-100 p-3': showBorder }">
<div class="prometheus-graph-header">
<h5 ref="graphTitle" class="prometheus-graph-title">{{ graphData.title }}</h5>
<gl-button
v-if="exportMetricsToCsvEnabled"
:href="downloadLink"
:title="__('Download CSV')"
:aria-label="__('Download CSV')"
style="margin-left: 200px;"
download="chart_metrics.csv"
>
{{ __('Download CSV') }}
</gl-button>
<div ref="graphWidgets" class="prometheus-graph-widgets"><slot></slot></div>
</div>
<gl-area-chart
......
......@@ -235,6 +235,19 @@ export default {
chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)),
);
},
csvText(graphData) {
const chartData = graphData.queries[0].result[0].values;
const yLabel = graphData.y_label;
const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings
return chartData.reduce((csv, data) => {
const row = data.join(',');
return `${csv}${row}\r\n`;
}, header);
},
downloadCsv(graphData) {
const data = new Blob([this.csvText(graphData)], { type: 'text/plain' });
return window.URL.createObjectURL(data);
},
// TODO: BEGIN, Duplicated code with panel_type until feature flag is removed
// Issue number: https://gitlab.com/gitlab-org/gitlab-ce/issues/63845
getGraphAlerts(queries) {
......@@ -448,7 +461,6 @@ export default {
@setAlerts="setAlerts"
/>
<gl-dropdown
v-if="alertWidgetAvailable"
v-gl-tooltip
class="mx-2"
toggle-class="btn btn-transparent border-0"
......@@ -459,6 +471,9 @@ export default {
<template slot="button-content">
<icon name="ellipsis_v" class="text-secondary" />
</template>
<gl-dropdown-item :href="downloadCsv(graphData)" download="chart_metrics.csv">
{{ __('Download CSV') }}
</gl-dropdown-item>
<gl-dropdown-item
v-if="alertWidgetAvailable"
v-gl-modal="`alert-modal-${index}-${graphIndex}`"
......
<script>
import { mapState } from 'vuex';
import _ from 'underscore';
import { GlDropdown, GlDropdownItem, GlModal, GlModalDirective } from '@gitlab/ui';
import {
GlDropdown,
GlDropdownItem,
GlModal,
GlModalDirective,
GlTooltipDirective,
} from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import MonitorAreaChart from './charts/area.vue';
import MonitorSingleStatChart from './charts/single_stat.vue';
import MonitorEmptyChart from './charts/empty_chart.vue';
......@@ -11,12 +18,14 @@ export default {
MonitorAreaChart,
MonitorSingleStatChart,
MonitorEmptyChart,
Icon,
GlDropdown,
GlDropdownItem,
GlModal,
},
directives: {
GlModal: GlModalDirective,
GlTooltip: GlTooltipDirective,
},
props: {
graphData: {
......@@ -41,6 +50,19 @@ export default {
graphDataHasMetrics() {
return this.graphData.queries[0].result.length > 0;
},
csvText() {
const chartData = this.graphData.queries[0].result[0].values;
const yLabel = this.graphData.y_label;
const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings
return chartData.reduce((csv, data) => {
const row = data.join(',');
return `${csv}${row}\r\n`;
}, header);
},
downloadCsv() {
const data = new Blob([this.csvText], { type: 'text/plain' });
return window.URL.createObjectURL(data);
},
},
methods: {
getGraphAlerts(queries) {
......@@ -81,7 +103,6 @@ export default {
@setAlerts="setAlerts"
/>
<gl-dropdown
v-if="alertWidgetAvailable"
v-gl-tooltip
class="mx-2"
toggle-class="btn btn-transparent border-0"
......@@ -92,6 +113,9 @@ export default {
<template slot="button-content">
<icon name="ellipsis_v" class="text-secondary" />
</template>
<gl-dropdown-item :href="downloadCsv" download="chart_metrics.csv">
{{ __('Download CSV') }}
</gl-dropdown-item>
<gl-dropdown-item v-if="alertWidgetAvailable" v-gl-modal="`alert-modal-${index}`">
{{ __('Alerts') }}
</gl-dropdown-item>
......
......@@ -13,7 +13,6 @@ export default (props = {}) => {
prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint,
multipleDashboardsEnabled: gon.features.environmentMetricsShowMultipleDashboards,
additionalPanelTypesEnabled: gon.features.environmentMetricsAdditionalPanelTypes,
exportMetricsToCsvEnabled: gon.features.exportMetricsToCsvEnabled,
});
}
......
......@@ -37,17 +37,11 @@ export const setEndpoints = ({ commit }, endpoints) => {
export const setFeatureFlags = (
{ commit },
{
prometheusEndpointEnabled,
multipleDashboardsEnabled,
additionalPanelTypesEnabled,
exportMetricsToCsvEnabled,
},
{ prometheusEndpointEnabled, multipleDashboardsEnabled, additionalPanelTypesEnabled },
) => {
commit(types.SET_DASHBOARD_ENABLED, prometheusEndpointEnabled);
commit(types.SET_MULTIPLE_DASHBOARDS_ENABLED, multipleDashboardsEnabled);
commit(types.SET_ADDITIONAL_PANEL_TYPES_ENABLED, additionalPanelTypesEnabled);
commit(types.SET_EXPORT_METRICS_TO_CSV_ENABLED, exportMetricsToCsvEnabled);
};
export const setShowErrorBanner = ({ commit }, enabled) => {
......
......@@ -17,4 +17,3 @@ export const SET_ENDPOINTS = 'SET_ENDPOINTS';
export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE';
export const SET_NO_DATA_EMPTY_STATE = 'SET_NO_DATA_EMPTY_STATE';
export const SET_SHOW_ERROR_BANNER = 'SET_SHOW_ERROR_BANNER';
export const SET_EXPORT_METRICS_TO_CSV_ENABLED = 'SET_EXPORT_METRICS_TO_CSV_ENABLED';
......@@ -99,7 +99,4 @@ export default {
[types.SET_SHOW_ERROR_BANNER](state, enabled) {
state.showErrorBanner = enabled;
},
[types.SET_EXPORT_METRICS_TO_CSV_ENABLED](state, enabled) {
state.exportMetricsToCsvEnabled = enabled;
},
};
......@@ -10,7 +10,6 @@ export default () => ({
useDashboardEndpoint: false,
multipleDashboardsEnabled: false,
additionalPanelTypesEnabled: false,
exportMetricsToCsvEnabled: false,
emptyState: 'gettingStarted',
showEmptyState: true,
showErrorBanner: true,
......
......@@ -23,7 +23,7 @@ export default {
};
</script>
<template>
<section class="mr-widget-help">
<section class="mr-widget-help font-italic">
<template v-if="missingBranch">
{{ missingBranchInfo }}
</template>
......
......@@ -18,8 +18,8 @@ export default {
Deployment,
MrWidgetContainer,
MrWidgetPipeline,
MergeTrainInfo: () =>
import('ee_component/vue_merge_request_widget/components/merge_train_info.vue'),
MergeTrainPositionIndicator: () =>
import('ee_component/vue_merge_request_widget/components/merge_train_position_indicator.vue'),
},
props: {
mr: {
......@@ -62,7 +62,7 @@ export default {
showVisualReviewAppLink() {
return this.mr.visualReviewAppAvailable;
},
showMergeTrainInfo() {
showMergeTrainPositionIndicator() {
return _.isNumber(this.mr.mergeTrainIndex);
},
},
......@@ -90,8 +90,8 @@ export default {
:visual-review-app-meta="visualReviewAppMeta"
/>
</div>
<merge-train-info
v-if="showMergeTrainInfo"
<merge-train-position-indicator
v-if="showMergeTrainPositionIndicator"
class="mr-widget-extension"
:merge-train-index="mr.mergeTrainIndex"
/>
......
......@@ -31,6 +31,9 @@ export default class MergeRequestStore {
this.targetBranchSha = data.target_branch_sha;
this.sourceBranch = data.source_branch;
this.sourceBranchProtected = data.source_branch_protected;
this.conflictsDocsPath = data.conflicts_docs_path;
this.mergeRequestPipelinesHelpPath = data.merge_request_pipelines_docs_path;
this.mergeTrainWhenPipelineSucceedsDocsPath = data.merge_train_when_pipeline_succeeds_docs_path;
this.mergeStatus = data.merge_status;
this.commitMessage = data.default_merge_commit_message;
this.shortMergeCommitSha = data.short_merge_commit_sha;
......
......@@ -397,7 +397,6 @@
.mr-widget-help {
padding: 10px 16px 10px ($gl-padding-8 * 7);
font-style: italic;
}
.ci-coverage {
......@@ -906,7 +905,7 @@
}
.deploy-heading,
.merge-train-info {
.merge-train-position-indicator {
@include media-breakpoint-up(md) {
padding: $gl-padding-8 $gl-padding;
}
......
# frozen_string_literal: true
module InvisibleCaptcha
extend ActiveSupport::Concern
included do
invisible_captcha only: :create, on_spam: :on_honeypot_spam_callback, on_timestamp_spam: :on_timestamp_spam_callback
end
def on_honeypot_spam_callback
return unless Feature.enabled?(:invisible_captcha)
invisible_captcha_honeypot_counter.increment
log_request('Invisible_Captcha_Honeypot_Request')
head(200)
end
def on_timestamp_spam_callback
return unless Feature.enabled?(:invisible_captcha)
invisible_captcha_timestamp_counter.increment
log_request('Invisible_Captcha_Timestamp_Request')
redirect_to new_user_session_path, alert: InvisibleCaptcha.timestamp_error_message
end
def invisible_captcha_honeypot_counter
@invisible_captcha_honeypot_counter ||=
Gitlab::Metrics.counter(:bot_blocked_by_invisible_captcha_honeypot,
'Counter of blocked sign up attempts with filled honeypot')
end
def invisible_captcha_timestamp_counter
@invisible_captcha_timestamp_counter ||=
Gitlab::Metrics.counter(:bot_blocked_by_invisible_captcha_timestamp,
'Counter of blocked sign up attempts with invalid timestamp')
end
def log_request(message)
request_information = {
message: message,
env: :invisible_captcha_signup_bot_detected,
ip: request.ip,
request_method: request.request_method,
fullpath: request.fullpath
}
Gitlab::AuthLogger.error(request_information)
end
end
......@@ -15,7 +15,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController
push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards)
push_frontend_feature_flag(:environment_metrics_additional_panel_types)
push_frontend_feature_flag(:prometheus_computed_alerts)
push_frontend_feature_flag(:export_metrics_to_csv_enabled)
end
def index
......
......@@ -4,6 +4,7 @@ class RegistrationsController < Devise::RegistrationsController
include Recaptcha::Verify
include AcceptsPendingInvitations
include RecaptchaExperimentHelper
include InvisibleCaptcha
prepend_before_action :check_captcha, only: :create
before_action :whitelist_query_limiting, only: [:destroy]
......
......@@ -270,7 +270,11 @@ module ApplicationSettingsHelper
:diff_max_patch_bytes,
:commit_email_hostname,
:protected_ci_variables,
:local_markdown_version
:local_markdown_version,
:snowplow_collector_hostname,
:snowplow_cookie_domain,
:snowplow_enabled,
:snowplow_site_id
]
end
......
# frozen_string_literal: true
module SessionsHelper
def unconfirmed_email?
flash[:alert] == t(:unconfirmed, scope: [:devise, :failure])
end
end
......@@ -2,8 +2,21 @@
module TrackingHelper
def tracking_attrs(label, event, property)
{} # CE has no tracking features
return {} unless tracking_enabled?
{
data: {
track_label: label,
track_event: event,
track_property: property
}
}
end
end
TrackingHelper.prepend_if_ee('EE::TrackingHelper')
private
def tracking_enabled?
Rails.env.production? &&
::Gitlab::CurrentSettings.snowplow_enabled?
end
end
......@@ -99,6 +99,11 @@ class ApplicationSetting < ApplicationRecord
presence: true,
if: :plantuml_enabled
validates :snowplow_collector_hostname,
presence: true,
hostname: true,
if: :snowplow_enabled
validates :max_attachment_size,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
......
......@@ -97,6 +97,10 @@ module ApplicationSettingImplementation
usage_stats_set_by_user_id: nil,
diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
commit_email_hostname: default_commit_email_hostname,
snowplow_collector_hostname: nil,
snowplow_cookie_domain: nil,
snowplow_enabled: false,
snowplow_site_id: nil,
protected_ci_variables: false,
local_markdown_version: 0,
outbound_local_requests_whitelist: [],
......
......@@ -64,11 +64,15 @@ module Clusters
end
def delete_private_key
"kubectl delete secret -n #{Gitlab::Kubernetes::Helm::NAMESPACE} #{private_key_name} --ignore-not-found" if private_key_name.present?
return unless private_key_name.present?
args = %W(secret -n #{Gitlab::Kubernetes::Helm::NAMESPACE} #{private_key_name} --ignore-not-found)
Gitlab::Kubernetes::KubectlCmd.delete(*args)
end
def delete_crd(definition)
"kubectl delete crd #{definition} --ignore-not-found"
Gitlab::Kubernetes::KubectlCmd.delete("crd", definition, "--ignore-not-found")
end
def cluster_issuer_file
......
......@@ -89,7 +89,7 @@ module Clusters
def delete_knative_services
cluster.kubernetes_namespaces.map do |kubernetes_namespace|
"kubectl delete ksvc --all -n #{kubernetes_namespace.namespace}"
Gitlab::Kubernetes::KubectlCmd.delete("ksvc", "--all", "-n", kubernetes_namespace.namespace)
end
end
......@@ -99,14 +99,14 @@ module Clusters
def delete_knative_namespaces
[
"kubectl delete --ignore-not-found ns knative-serving",
"kubectl delete --ignore-not-found ns knative-build"
Gitlab::Kubernetes::KubectlCmd.delete("--ignore-not-found", "ns", "knative-serving"),
Gitlab::Kubernetes::KubectlCmd.delete("--ignore-not-found", "ns", "knative-build")
]
end
def delete_knative_and_istio_crds
api_resources.map do |crd|
"kubectl delete --ignore-not-found crd #{crd}"
Gitlab::Kubernetes::KubectlCmd.delete("--ignore-not-found", "crd", "#{crd}")
end
end
......@@ -119,13 +119,13 @@ module Clusters
def install_knative_metrics
return [] unless cluster.application_prometheus_available?
["kubectl apply -f #{METRICS_CONFIG}"]
[Gitlab::Kubernetes::KubectlCmd.apply_file(METRICS_CONFIG)]
end
def delete_knative_istio_metrics
return [] unless cluster.application_prometheus_available?
["kubectl delete --ignore-not-found -f #{METRICS_CONFIG}"]
[Gitlab::Kubernetes::KubectlCmd.delete("--ignore-not-found", "-f", METRICS_CONFIG)]
end
def verify_cluster?
......
......@@ -106,13 +106,13 @@ module Clusters
def install_knative_metrics
return [] unless cluster.application_knative_available?
["kubectl apply -f #{Clusters::Applications::Knative::METRICS_CONFIG}"]
[Gitlab::Kubernetes::KubectlCmd.apply_file(Clusters::Applications::Knative::METRICS_CONFIG)]
end
def delete_knative_istio_metrics
return [] unless cluster.application_knative_available?
["kubectl delete -f #{Clusters::Applications::Knative::METRICS_CONFIG}"]
[Gitlab::Kubernetes::KubectlCmd.delete("-f", Clusters::Applications::Knative::METRICS_CONFIG)]
end
end
end
......
......@@ -35,6 +35,8 @@ class SlashCommandsService < Service
chat_user = find_chat_user(params)
if chat_user&.user
return Gitlab::SlashCommands::Presenters::Access.new.access_denied unless chat_user.user.can?(:use_slash_commands)
Gitlab::SlashCommands::Command.new(project, chat_user, params).execute
else
url = authorize_chat_name_url(params)
......
......@@ -33,6 +33,7 @@ class GlobalPolicy < BasePolicy
enable :access_git
enable :receive_notifications
enable :use_quick_actions
enable :use_slash_commands
end
rule { blocked | internal }.policy do
......@@ -40,6 +41,7 @@ class GlobalPolicy < BasePolicy
prevent :access_api
prevent :access_git
prevent :receive_notifications
prevent :use_slash_commands
end
rule { required_terms_not_accepted }.policy do
......@@ -57,6 +59,7 @@ class GlobalPolicy < BasePolicy
rule { access_locked }.policy do
prevent :log_in
prevent :use_slash_commands
end
rule { ~(anonymous & restricted_public_level) }.policy do
......
- expanded = true if !@application_setting.valid? && @application_setting.errors.any? { |k| k.to_s.start_with?('snowplow_') }
%section.settings.as-snowplow.no-animate#js-snowplow-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
Snowplow
= _('Snowplow')
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
= _('Configure the %{link} integration.').html_safe % { link: link_to('Snowplow', 'https://snowplowanalytics.com/', target: '_blank') }
.settings-content
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
= form_for @application_setting, url: integrations_admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
.form-check
= f.check_box :snowplow_enabled, class: 'form-check-input'
= f.label :snowplow_enabled, class: 'form-check-label' do
Enable Snowplow
= f.label :snowplow_enabled, _('Enable snowplow tracking'), class: 'form-check-label'
.form-group
= f.label :snowplow_collector_hostname, 'Collector Hostname', class: 'label-light'
= f.label :snowplow_collector_hostname, _('Collector hostname'), class: 'label-light'
= f.text_field :snowplow_collector_hostname, class: 'form-control', placeholder: 'snowplow.example.com'
.form-group
= f.label :snowplow_site_id, class: 'label-light' do
Site ID
= f.label :snowplow_site_id, _('Site ID'), class: 'label-light'
= f.text_field :snowplow_site_id, class: 'form-control'
.form-group
= f.label :snowplow_cookie_domain, class: 'label-light' do
Cookie domain
= f.label :snowplow_cookie_domain, _('Cookie domain'), class: 'label-light'
= f.text_field :snowplow_cookie_domain, class: 'form-control'
= f.submit 'Save changes', class: 'btn btn-success'
= f.submit _('Save changes'), class: 'btn btn-success'
= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-field-errors', 'aria-live' => 'assertive'}) do |f|
.form-group
= f.label "Username or email", for: "user_login", class: 'label-bold'
= f.text_field :login, class: "form-control top", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required.", data: { qa_selector: 'login_field' }
= f.label _('Username or email'), for: 'user_login', class: 'label-bold'
= f.text_field :login, class: 'form-control top', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', required: true, title: _('This field is required.'), data: { qa_selector: 'login_field' }
.form-group
= f.label :password, class: 'label-bold'
= f.password_field :password, class: "form-control bottom", required: true, title: "This field is required.", data: { qa_selector: 'password_field' }
= f.password_field :password, class: 'form-control bottom', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field' }
- if devise_mapping.rememberable?
.remember-me
%label{ for: "user_remember_me" }
%label{ for: 'user_remember_me' }
= f.check_box :remember_me, class: 'remember-me-checkbox'
%span Remember me
.float-right.forgot-password
= link_to "Forgot your password?", new_password_path(:user)
.float-right
- if unconfirmed_email?
= link_to _('Resend confirmation email'), new_user_confirmation_path
- else
= link_to _('Forgot your password?'), new_password_path(:user)
%div
- if captcha_enabled?
= recaptcha_tags
.submit-container.move-submit-down
= f.submit "Sign in", class: "btn btn-success", data: { qa_selector: 'sign_in_button' }
= f.submit _('Sign in'), class: 'btn btn-success', data: { qa_selector: 'sign_in_button' }
......@@ -5,6 +5,8 @@
= form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_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)
= invisible_captcha
.name.form-group
= f.label :name, _('Full name'), class: 'label-bold'
= f.text_field :name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_name_length, :max_length_message => s_("SignUp|Name is too long (maximum is %{max_length} characters).") % { max_length: max_name_length }, :qa_selector => 'new_user_name_field' }, required: true, title: _("This field is required.")
......
......@@ -14,13 +14,16 @@
respectDoNotTrack: true,
forceSecureTracker: true,
post: true,
contexts: {
webPage: true,
},
contexts: { webPage: true },
stateStorageStrategy: "localStorage"
});
window.snowplow('enableActivityTracking', 30, 30);
window.snowplow('trackPageView');
= render 'layouts/snowplow_additional_tracking'
- return unless Feature.enabled?(:additional_snowplow_tracking, @group)
= javascript_tag nonce: true do
:plain
window.snowplow('enableFormTracking');
window.snowplow('enableLinkClickTracking');
......@@ -45,20 +45,20 @@
.form-group
= f.label :layout, class: 'label-bold' do
= s_('Preferences|Layout width')
= f.select :layout, layout_choices, {}, class: 'form-control'
= f.select :layout, layout_choices, {}, class: 'select2'
.form-text.text-muted
= s_('Preferences|Choose between fixed (max. 1280px) and fluid (100%%) application layout.')
.form-group
= f.label :dashboard, class: 'label-bold' do
= s_('Preferences|Default dashboard')
= f.select :dashboard, dashboard_choices, {}, class: 'form-control'
= f.select :dashboard, dashboard_choices, {}, class: 'select2'
= render_if_exists 'profiles/preferences/group_overview_selector', f: f # EE-specific
.form-group
= f.label :project_view, class: 'label-bold' do
= s_('Preferences|Project overview content')
= f.select :project_view, project_view_choices, {}, class: 'form-control'
= f.select :project_view, project_view_choices, {}, class: 'select2'
.form-text.text-muted
= s_('Preferences|Choose what content you want to see on a project’s overview page.')
......@@ -82,7 +82,7 @@
.form-group
= f.label :first_day_of_week, class: 'label-bold' do
= _('First day of the week')
= f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'form-control'
= f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'select2'
- if Feature.enabled?(:user_time_settings)
.col-sm-12
%hr
......
---
title: Harmonize selections in user settings
merge_request: 31110
author: Marc Schwede
type: other
---
title: 'feat: adds a download to csv functionality to the dropdown in prometheus metrics'
merge_request: 31679
author:
type: changed
---
title: Allow users to resend a confirmation link when the grace period has expired
merge_request: 31476
author:
type: changed
---
title: Migrate remaining users with null private_profile
merge_request: 31708
author:
type: other
---
title: Restrict slash commands to users who can log in
merge_request:
author:
type: security
# frozen_string_literal: true
InvisibleCaptcha.setup do |config|
config.honeypots = %w(firstname lastname)
config.timestamp_enabled = true
config.timestamp_threshold = 4
end
en:
invisible_captcha:
sentence_for_humans: If you are human, please ignore this field.
timestamp_error_message: That was a bit too quick! Please resubmit.
# frozen_string_literal: true
class MigratePrivateProfileNulls < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
DELAY = 5.minutes.to_i
BATCH_SIZE = 1_000
disable_ddl_transaction!
class User < ActiveRecord::Base
self.table_name = 'users'
include ::EachBatch
end
def up
# Migration will take about 7 hours
User.where(private_profile: nil).each_batch(of: BATCH_SIZE) do |batch, index|
range = batch.pluck(Arel.sql("MIN(id)"), Arel.sql("MAX(id)")).first
delay = index * DELAY
BackgroundMigrationWorker.perform_in(delay.seconds, 'MigrateNullPrivateProfileToFalse', [*range])
end
end
def down
# noop
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_08_06_071559) do
ActiveRecord::Schema.define(version: 2019_08_12_070645) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
......
......@@ -354,6 +354,7 @@ The following documentation relates to the DevOps **Secure** stage:
| Secure Topics | Description |
|:------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------|
| [Container Scanning](user/application_security/container_scanning/index.md) **(ULTIMATE)** | Use Clair to scan docker images for known vulnerabilities. |
| [Dependency List](user/application_security/dependency_list/index.md) **(ULTIMATE)** | View your project's dependencies and their known vulnerabilities. |
| [Dependency Scanning](user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. |
| [Dynamic Application Security Testing (DAST)](user/application_security/dast/index.md) **(ULTIMATE)** | Analyze running web applications for known vulnerabilities. |
| [Group Security Dashboard](user/application_security/security_dashboard/index.md) **(ULTIMATE)** | View vulnerabilities in all the projects in a group and its subgroups. |
......
......@@ -11,7 +11,7 @@ Every call to this endpoint requires authentication. To perform this call, user
## List project dependencies
Get a list of project dependencies. This API partially mirroring
[Dependency List](../user/application_security/dependency_scanning/index.md#dependency-list) feature.
[Dependency List](../user/application_security/dependency_list/index.md) feature.
This list can be generated only for [languages and package managers](../user/application_security/dependency_scanning/index.md#supported-languages-and-package-managers)
supported by Gemnasium.
......
......@@ -321,4 +321,8 @@ are listed in the descriptions of the relevant settings.
| `user_show_add_ssh_key_message` | boolean | no | When set to `false` disable the "You won't be able to pull or push project code via SSH" warning shown to users with no uploaded SSH key. |
| `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. |
| `local_markdown_version` | integer | no | Increase this value when any cached markdown should be invalidated. |
| `snowplow_enabled` | boolean | no | Enable snowplow tracking. |
| `snowplow_collector_hostname` | string | required by: `snowplow_enabled` | The Snowplow collector hostname. (e.g. `snowplow.trx.gitlab.net`) |
| `snowplow_site_id` | string | no | The Snowplow site name / application id. (e.g. `gitlab`) |
| `snowplow_cookie_domain` | string | no | The Snowplow cookie domain. (e.g. `.gitlab.com`) |
| `geo_node_allowed_ips` | string | yes | **(PREMIUM)** Comma-separated list of IPs and CIDRs of allowed secondary nodes. For example, `1.1.1.1, 2.2.2.0/24`. |
---
redirect_to: '../user/project/description_templates.md#setting-a-default-template-for-issues-and-merge-requests--starter'
redirect_to: '../user/project/description_templates.md#setting-a-default-template-for-merge-requests-and-issues--starter'
---
This document was moved to [description_templates](../user/project/description_templates.md#setting-a-default-template-for-issues-and-merge-requests--starter).
This document was moved to [description_templates](../user/project/description_templates.md#setting-a-default-template-for-merge-requests-and-issues--starter).
......@@ -171,6 +171,19 @@ Now, every time you create an MR for CE and EE:
job failed, you are required to submit the EE MR so that you can fix the conflicts in EE
before merging your changes into CE.
## How we run the Automatic CE->EE merge at GitLab
At GitLab, we use the [Merge Train](https://gitlab.com/gitlab-org/merge-train)
project to keep our [gitlab-ee](https://gitlab.com/gitlab-org/gitlab-ee)
repository updated with commits from
[gitlab-ce](https://gitlab.com/gitlab-org/gitlab-ce).
We have a mirror of the [Merge Train](https://gitlab.com/gitlab-org/merge-train)
project [configured](https://ops.gitlab.net/gitlab-org/merge-train) to run an
automatic CE->EE merge job every twenty minutes as a scheduled CI job. The
[configured](https://ops.gitlab.net/gitlab-org/merge-train) Merge Train project
is only accessible to authorized GitLab staff.
## FAQ
### How does automatic merging work?
......
......@@ -877,10 +877,10 @@ Other text includes deprecation notices and version-specific how-to information.
When a feature is available in EE-only tiers, add the corresponding tier according to the
feature availability:
- For GitLab Core and GitLab.com Free: `**(CORE)**`.
- For GitLab Starter and GitLab.com Bronze: `**(STARTER)**`.
- For GitLab Premium and GitLab.com Silver: `**(PREMIUM)**`.
- For GitLab Ultimate and GitLab.com Gold: `**(ULTIMATE)**`.
- For GitLab Core and GitLab.com Free: `**(CORE)**`.
To exclude GitLab.com tiers (when the feature is not available in GitLab.com), add the
keyword "only":
......@@ -892,6 +892,7 @@ keyword "only":
For GitLab.com only tiers (when the feature is not available for self-hosted instances):
- For GitLab Free and higher tiers: `**(FREE ONLY)**`.
- For GitLab Bronze and higher tiers: `**(BRONZE ONLY)**`.
- For GitLab Silver and higher tiers: `**(SILVER ONLY)**`.
- For GitLab Gold: `**(GOLD ONLY)**`.
......
......@@ -70,6 +70,7 @@ bundle exec rspec spec/[path]/[to]/[spec].rb
- On `before` and `after` hooks, prefer it scoped to `:context` over `:all`
- When using `evaluate_script("$('.js-foo').testSomething()")` (or `execute_script`) which acts on a given element,
use a Capyabara matcher beforehand (e.g. `find('.js-foo')`) to ensure the element actually exists.
- Use `focus: true` to isolate parts of the specs you want to run.
[four-phase-test]: https://robots.thoughtbot.com/four-phase-test
......
......@@ -202,8 +202,8 @@ Then select 'Internet Site' and press enter to confirm the hostname.
The Ruby interpreter is required to run GitLab.
**Note:** The current supported Ruby (MRI) version is 2.5.x. GitLab 11.6
dropped support for Ruby 2.4.x.
**Note:** The current supported Ruby (MRI) version is 2.6.x. GitLab 12.2
dropped support for Ruby 2.5.x.
The use of Ruby version managers such as [RVM], [rbenv] or [chruby] with GitLab
in production, frequently leads to hard to diagnose problems. For example,
......
......@@ -40,7 +40,7 @@ Please consider using a virtual machine to run GitLab.
## Ruby versions
GitLab requires Ruby (MRI) 2.5. Support for Ruby versions below 2.5 (2.3, 2.4) will stop with GitLab 11.6.
GitLab requires Ruby (MRI) 2.6. Support for Ruby versions below 2.6 (2.4, 2.5) will stop with GitLab 12.2.
You will have to use the standard MRI implementation of Ruby.
We love [JRuby](https://www.jruby.org/) and [Rubinius](https://rubinius.com) but GitLab
......
......@@ -47,8 +47,8 @@ sudo service gitlab stop
### 3. Update Ruby
NOTE: Beginning in GitLab 11.6, we only support Ruby 2.5 or higher, and dropped
support for Ruby 2.4. Be sure to upgrade if necessary.
NOTE: Beginning in GitLab 12.2, we only support Ruby 2.6 or higher, and dropped
support for Ruby 2.5. Be sure to upgrade if necessary.
You can check which version you are running with `ruby -v`.
......
# Productivity Analytics **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/12079) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.2 (enabled by feature flags `analytics` and `productivity_analytics`).
Track development velocity with Productivity Analytics.
For many companies, the development cycle is a blackbox and getting an estimate of how
long, on average, it takes to deliver features is an enormous endeavor.
While [Cycle Analytics](../project/cycle_analytics.md) focuses on the entire
SDLC process, Productivity Analytics provides a way for Engineering Management to
drill down in a systematic way to uncover patterns and causes for success or failure at
an individual, project or group level.
Productivity can slow down for many reasons ranging from degrading code base to quickly
growing teams. In order to investigate, department or team leaders can start by visualizing the time
it takes for merge requests to be merged.
## Supported features
Productivity Analytics allows GitLab users to:
- Visualize typical Merge Request lifetime and statistics. Use a histogram
that shows the distribution of the time elapsed between creating and merging
Merge Requests.
- Drill down into the most time consuming Merge Requests, select a number of outliers,
and filter down all subsequent charts to investigate potential causes.
- Filter by group, project, author, label, milestone, or a specific date range.
Filter down, for example, to the Merge Requests of a specific author in a group
or project during a milestone or specific date range.
## Accessing metrics and visualizations
To access the **Productivity Analytics** page, go to **Analytics > Productivity Analytics**.
The following metrics and visualizations are available on a project or group level:
- Histogram showing the number of Merge Request that took a specified number of days to
merge after creation. Select a specific column to filter down subsequent charts.
- Histogram showing a breakdown of the time taken (in hours) to merge a Merge Request.
The following intervals are available:
- Time from first commit to first comment.
- Time from first comment until last commit.
- Time from last commit to merge.
- Histogram showing the size or complexity of a Merge Request, using the following:
- Number of commits per Merge Request.
- Number of lines of code per commit.
- Number of files touched.
- Table showing list of Merge Requests with their respective times and size metrics.
Can be sorted by the above metrics.
- Users can sort by any of the above metrics
## Retrieving data
Users can retrieve three months of data when they deploy Productivity Analytics for the first time.
To retrieve data for a different time span, run the following in the GitLab directory:
```sh
MERGED_AT_AFTER = <your_date> rake gitlab:productivity_analytics:recalc
```
## Permissions
The **Productivity Analytics** dashboard can be accessed only:
- On GitLab instances and namespaces on
[Premium or Silver tier](https://about.gitlab.com/pricing/) and above.
- By users with [Reporter access](../permissions.md) and above.
# Dependency List **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/10075) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
The Dependency list allows you to see your project's dependencies, and key
details about them, including their known vulnerabilities. To see it,
navigate to **Security & Compliance > Dependency List** in your project's
sidebar.
## Requirements
1. The [Dependency Scanning](../dependency_scanning/index.md) CI job must be
configured for your project.
1. Your project uses at least one of the
[languages and package managers](../dependency_scanning/index.md#supported-languages-and-package-managers)
supported by Gemnasium.
## Viewing dependencies
![Dependency List](img/dependency_list_v12_2.png)
Dependencies are displayed with the following information:
| Field | Description |
| --------- | ----------- |
| Status | Displays whether or not the dependency has any known vulnerabilities |
| Component | The dependency's name |
| Version | The exact locked version of the dependency your project uses |