summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-03 09:07:54 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-03 09:07:54 +0000
commit87ef501eacd66d7166183d20d84e33de022f7002 (patch)
treefa4e0f41e00a4b6aeb035530be4b5473f51b7a3d /app
parentf321e51f46bcb628c3e96a44b5ebf3bb1c4033ab (diff)
downloadgitlab-ce-87ef501eacd66d7166183d20d84e33de022f7002.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/error_tracking/components/error_details.vue23
-rw-r--r--app/assets/javascripts/error_tracking/components/error_tracking_list.vue1
-rw-r--r--app/assets/javascripts/monitoring/components/charts/options.js78
-rw-r--r--app/assets/javascripts/monitoring/components/charts/time_series.vue24
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue20
-rw-r--r--app/assets/javascripts/monitoring/stores/utils.js27
-rw-r--r--app/assets/javascripts/terminal/terminal.js4
-rw-r--r--app/assets/javascripts/u2f/authenticate.js6
-rw-r--r--app/assets/javascripts/u2f/register.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js16
-rw-r--r--app/assets/stylesheets/pages/environments.scss8
-rw-r--r--app/controllers/profiles_controller.rb1
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb2
-rw-r--r--app/models/appearance.rb2
-rw-r--r--app/models/application_setting_implementation.rb2
-rw-r--r--app/models/ci/job_artifact.rb2
-rw-r--r--app/models/clusters/applications/ingress.rb2
-rw-r--r--app/models/clusters/cluster.rb4
-rw-r--r--app/models/concerns/has_repository.rb2
-rw-r--r--app/models/concerns/milestoneable.rb2
-rw-r--r--app/models/concerns/time_trackable.rb2
-rw-r--r--app/models/deploy_token.rb2
-rw-r--r--app/models/description_version.rb8
-rw-r--r--app/models/external_pull_request.rb2
-rw-r--r--app/models/milestone_release.rb2
-rw-r--r--app/models/namespace.rb2
-rw-r--r--app/models/project_ci_cd_setting.rb2
-rw-r--r--app/models/project_services/issue_tracker_service.rb2
-rw-r--r--app/models/prometheus_alert.rb4
-rw-r--r--app/models/resource_event.rb5
-rw-r--r--app/models/sent_notification.rb5
-rw-r--r--app/models/timelog.rb4
-rw-r--r--app/models/user.rb7
-rw-r--r--app/models/user_detail.rb7
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml9
36 files changed, 225 insertions, 69 deletions
diff --git a/app/assets/javascripts/error_tracking/components/error_details.vue b/app/assets/javascripts/error_tracking/components/error_details.vue
index 89d32efec6d..093d993c3ad 100644
--- a/app/assets/javascripts/error_tracking/components/error_details.vue
+++ b/app/assets/javascripts/error_tracking/components/error_details.vue
@@ -15,7 +15,6 @@ import {
GlDropdownDivider,
} from '@gitlab/ui';
import { __, sprintf, n__ } from '~/locale';
-import LoadingButton from '~/vue_shared/components/loading_button.vue';
import Icon from '~/vue_shared/components/icon.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import Stacktrace from './stacktrace.vue';
@@ -28,7 +27,6 @@ import query from '../queries/details.query.graphql';
export default {
components: {
- LoadingButton,
GlButton,
GlFormInput,
GlLink,
@@ -234,19 +232,21 @@ export default {
</div>
<div class="error-details-actions">
<div class="d-inline-flex bv-d-sm-down-none">
- <loading-button
- :label="ignoreBtnLabel"
+ <gl-button
:loading="updatingIgnoreStatus"
data-qa-selector="update_ignore_status_button"
@click="onIgnoreStatusUpdate"
- />
- <loading-button
+ >
+ {{ ignoreBtnLabel }}
+ </gl-button>
+ <gl-button
class="btn-outline-info ml-2"
- :label="resolveBtnLabel"
:loading="updatingResolveStatus"
data-qa-selector="update_resolve_status_button"
@click="onResolveStatusUpdate"
- />
+ >
+ {{ resolveBtnLabel }}
+ </gl-button>
<gl-button
v-if="error.gitlabIssuePath"
class="ml-2"
@@ -270,14 +270,15 @@ export default {
name="issue[sentry_issue_attributes][sentry_issue_identifier]"
/>
<gl-form-input :value="csrfToken" class="hidden" name="authenticity_token" />
- <loading-button
+ <gl-button
v-if="!error.gitlabIssuePath"
class="btn-success"
- :label="__('Create issue')"
:loading="issueCreationInProgress"
data-qa-selector="create_issue_button"
@click="createIssue"
- />
+ >
+ {{ __('Create issue') }}
+ </gl-button>
</form>
</div>
<gl-dropdown
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
index 70f257180c6..552e8cac3a7 100644
--- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
+++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
@@ -236,6 +236,7 @@ export default {
</gl-dropdown>
<div class="filtered-search-input-container flex-fill">
<gl-form-input
+ v-model="errorSearchQuery"
class="pl-2 filtered-search"
:disabled="loading"
:placeholder="__('Search or filter results…')"
diff --git a/app/assets/javascripts/monitoring/components/charts/options.js b/app/assets/javascripts/monitoring/components/charts/options.js
new file mode 100644
index 00000000000..d9f49bd81f5
--- /dev/null
+++ b/app/assets/javascripts/monitoring/components/charts/options.js
@@ -0,0 +1,78 @@
+import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format';
+import { s__ } from '~/locale';
+
+const yAxisBoundaryGap = [0.1, 0.1];
+/**
+ * Max string length of formatted axis tick
+ */
+const maxDataAxisTickLength = 8;
+
+// Defaults
+const defaultFormat = SUPPORTED_FORMATS.number;
+
+const defaultYAxisFormat = defaultFormat;
+const defaultYAxisPrecision = 2;
+
+const defaultTooltipFormat = defaultFormat;
+const defaultTooltipPrecision = 3;
+
+// Give enough space for y-axis with units and name.
+const chartGridLeft = 75;
+
+// Axis options
+
+/**
+ * Converts .yml parameters to echarts axis options for data axis
+ * @param {Object} param - Dashboard .yml definition options
+ */
+const getDataAxisOptions = ({ format, precision, name }) => {
+ const formatter = getFormatter(format);
+
+ return {
+ name,
+ nameLocation: 'center', // same as gitlab-ui's default
+ scale: true,
+ axisLabel: {
+ formatter: val => formatter(val, precision, maxDataAxisTickLength),
+ },
+ };
+};
+
+/**
+ * Converts .yml parameters to echarts y-axis options
+ * @param {Object} param - Dashboard .yml definition options
+ */
+export const getYAxisOptions = ({
+ name = s__('Metrics|Values'),
+ format = defaultYAxisFormat,
+ precision = defaultYAxisPrecision,
+} = {}) => {
+ return {
+ nameGap: 63, // larger gap than gitlab-ui's default to fit with formatted numbers
+ scale: true,
+ boundaryGap: yAxisBoundaryGap,
+
+ ...getDataAxisOptions({
+ name,
+ format,
+ precision,
+ }),
+ };
+};
+
+// Chart grid
+
+/**
+ * Grid with enough room to display chart.
+ */
+export const getChartGrid = ({ left = chartGridLeft } = {}) => ({ left });
+
+// Tooltip options
+
+export const getTooltipFormatter = ({
+ format = defaultTooltipFormat,
+ precision = defaultTooltipPrecision,
+} = {}) => {
+ const formatter = getFormatter(format);
+ return num => formatter(num, precision);
+};
diff --git a/app/assets/javascripts/monitoring/components/charts/time_series.vue b/app/assets/javascripts/monitoring/components/charts/time_series.vue
index 1c39fb072d9..cba0a6da6a9 100644
--- a/app/assets/javascripts/monitoring/components/charts/time_series.vue
+++ b/app/assets/javascripts/monitoring/components/charts/time_series.vue
@@ -4,7 +4,6 @@ import { GlLink, GlButton, GlTooltip, GlResizeObserverDirective } from '@gitlab/
import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
import dateFormat from 'dateformat';
import { s__, __ } from '~/locale';
-import { getFormatter } from '~/lib/utils/unit_format';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import Icon from '~/vue_shared/components/icon.vue';
import {
@@ -16,6 +15,7 @@ import {
dateFormats,
chartColorValues,
} from '../../constants';
+import { getYAxisOptions, getChartGrid, getTooltipFormatter } from './options';
import { makeDataSeries } from '~/helpers/monitor_helper';
import { graphDataValidatorForValues } from '../../utils';
@@ -30,15 +30,13 @@ const deploymentYAxisCoords = {
max: 100,
};
-const THROTTLED_DATAZOOM_WAIT = 1000; // miliseconds
+const THROTTLED_DATAZOOM_WAIT = 1000; // milliseconds
const timestampToISODate = timestamp => new Date(timestamp).toISOString();
const events = {
datazoom: 'datazoom',
};
-const yValFormatter = getFormatter('number');
-
export default {
components: {
GlAreaChart,
@@ -167,14 +165,7 @@ export default {
const option = omit(this.option, ['series', 'yAxis', 'xAxis']);
const dataYAxis = {
- name: this.yAxisLabel,
- nameGap: 50, // same as gitlab-ui's default
- nameLocation: 'center', // same as gitlab-ui's default
- boundaryGap: [0.1, 0.1],
- scale: true,
- axisLabel: {
- formatter: num => yValFormatter(num, 3),
- },
+ ...getYAxisOptions(this.graphData.yAxis),
...yAxis,
};
@@ -204,6 +195,7 @@ export default {
series: this.chartOptionSeries,
xAxis: timeXAxis,
yAxis: [dataYAxis, deploymentsYAxis],
+ grid: getChartGrid(),
dataZoom: [this.dataZoomConfig],
...option,
};
@@ -282,8 +274,9 @@ export default {
},
};
},
- yAxisLabel() {
- return `${this.graphData.y_label}`;
+ tooltipYFormatter() {
+ // Use same format as y-axis
+ return getTooltipFormatter({ format: this.graphData.yAxis?.format });
},
},
created() {
@@ -315,12 +308,11 @@ export default {
this.tooltip.commitUrl = deploy.commitUrl;
} else {
const { seriesName, color, dataIndex } = dataPoint;
- const value = yValFormatter(yVal, 3);
this.tooltip.content.push({
name: seriesName,
dataIndex,
- value,
+ value: this.tooltipYFormatter(yVal),
color,
});
}
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index a4073133028..a0bd45bef5e 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -19,7 +19,7 @@ import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { s__ } from '~/locale';
import createFlash from '~/flash';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
+import { mergeUrlParams, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url';
import Icon from '~/vue_shared/components/icon.vue';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
@@ -351,6 +351,10 @@ export default {
};
redirectTo(mergeUrlParams(params, window.location.href));
},
+
+ refreshDashboard() {
+ refreshCurrentPage();
+ },
},
addMetric: {
title: s__('Metrics|Add metric'),
@@ -438,7 +442,7 @@ export default {
:label="s__('Metrics|Show last')"
label-size="sm"
label-for="monitor-time-window-dropdown"
- class="col-sm-6 col-md-6 col-lg-4"
+ class="col-sm-auto col-md-auto col-lg-auto"
>
<date-time-picker
ref="dateTimePicker"
@@ -449,6 +453,18 @@ export default {
/>
</gl-form-group>
+ <gl-form-group class="col-sm-2 col-md-2 col-lg-1 refresh-dashboard-button">
+ <gl-button
+ ref="refreshDashboardBtn"
+ v-gl-tooltip
+ variant="default"
+ :title="s__('Metrics|Reload this page')"
+ @click="refreshDashboard"
+ >
+ <icon name="repeat" />
+ </gl-button>
+ </gl-form-group>
+
<gl-form-group
v-if="hasHeaderButtons"
label-for="prometheus-graphs-dropdown-buttons"
diff --git a/app/assets/javascripts/monitoring/stores/utils.js b/app/assets/javascripts/monitoring/stores/utils.js
index 82deaa7ccfd..0e97d50f317 100644
--- a/app/assets/javascripts/monitoring/stores/utils.js
+++ b/app/assets/javascripts/monitoring/stores/utils.js
@@ -1,5 +1,6 @@
import { slugify } from '~/lib/utils/text_utility';
import createGqClient, { fetchPolicies } from '~/lib/graphql';
+import { SUPPORTED_FORMATS } from '~/lib/utils/unit_format';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
export const gqClient = createGqClient(
@@ -75,17 +76,37 @@ const mapToMetricsViewModel = (metrics, defaultLabel) =>
}));
/**
+ * Maps an axis view model
+ *
+ * Defaults to a 2 digit precision and `number` format. It only allows
+ * formats in the SUPPORTED_FORMATS array.
+ *
+ * @param {Object} axis
+ */
+const mapToAxisViewModel = ({ name = '', format = SUPPORTED_FORMATS.number, precision = 2 }) => {
+ return {
+ name,
+ format: SUPPORTED_FORMATS[format] || SUPPORTED_FORMATS.number,
+ precision,
+ };
+};
+
+/**
* Maps a metrics panel to its view model
*
* @param {Object} panel - Metrics panel
* @returns {Object}
*/
-const mapToPanelViewModel = ({ title = '', type, y_label, metrics = [] }) => {
+const mapToPanelViewModel = ({ title = '', type, y_label, y_axis = {}, metrics = [] }) => {
+ // Both `y_axis.name` and `y_label` are supported for now
+ // https://gitlab.com/gitlab-org/gitlab/issues/208385
+ const yAxis = mapToAxisViewModel({ name: y_label, ...y_axis }); // eslint-disable-line babel/camelcase
return {
title,
type,
- y_label,
- metrics: mapToMetricsViewModel(metrics, y_label),
+ y_label: yAxis.name, // Changing y_label to yLabel is pending https://gitlab.com/gitlab-org/gitlab/issues/207198
+ yAxis,
+ metrics: mapToMetricsViewModel(metrics, yAxis.name),
};
};
diff --git a/app/assets/javascripts/terminal/terminal.js b/app/assets/javascripts/terminal/terminal.js
index 9c7c10d9864..f4e546e4d4e 100644
--- a/app/assets/javascripts/terminal/terminal.js
+++ b/app/assets/javascripts/terminal/terminal.js
@@ -1,4 +1,4 @@
-import _ from 'underscore';
+import { throttle } from 'lodash';
import $ from 'jquery';
import { Terminal } from 'xterm';
import * as fit from 'xterm/lib/addons/fit/fit';
@@ -85,7 +85,7 @@ export default class GLTerminal {
addScrollListener(onScrollLimit) {
const viewport = this.container.querySelector('.xterm-viewport');
- const listener = _.throttle(() => {
+ const listener = throttle(() => {
onScrollLimit({
canScrollUp: canScrollUp(viewport, SCROLL_MARGIN),
canScrollDown: canScrollDown(viewport, SCROLL_MARGIN),
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js
index abfc81e681e..6244df1180e 100644
--- a/app/assets/javascripts/u2f/authenticate.js
+++ b/app/assets/javascripts/u2f/authenticate.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import _ from 'underscore';
+import { template as lodashTemplate, omit } from 'lodash';
import importU2FLibrary from './util';
import U2FError from './error';
@@ -37,7 +37,7 @@ export default class U2FAuthenticate {
// Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
// This can be removed once we upgrade.
// https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
- this.signRequests = u2fParams.sign_requests.map(request => _(request).omit('challenge'));
+ this.signRequests = u2fParams.sign_requests.map(request => omit(request, 'challenge'));
this.templates = {
setup: '#js-authenticate-u2f-setup',
@@ -74,7 +74,7 @@ export default class U2FAuthenticate {
renderTemplate(name, params) {
const templateString = $(this.templates[name]).html();
- const template = _.template(templateString);
+ const template = lodashTemplate(templateString);
return this.container.html(template(params));
}
diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js
index 43c814c8070..f5a422727ad 100644
--- a/app/assets/javascripts/u2f/register.js
+++ b/app/assets/javascripts/u2f/register.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import _ from 'underscore';
+import { template as lodashTemplate } from 'lodash';
import importU2FLibrary from './util';
import U2FError from './error';
@@ -59,7 +59,7 @@ export default class U2FRegister {
renderTemplate(name, params) {
const templateString = $(this.templates[name]).html();
- const template = _.template(templateString);
+ const template = lodashTemplate(templateString);
return this.container.html(template(params));
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
index 8e8e67228ed..ad80a51c5f9 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
@@ -53,6 +53,7 @@ export default {
.then(res => res.data)
.then(data => {
eventHub.$emit('UpdateWidgetData', data);
+ eventHub.$emit('MRWidgetUpdateRequested');
})
.catch(() => {
this.isCancellingAutoMerge = false;
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index ea83c61e275..91ac23f427d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -123,13 +123,15 @@ export default class MergeRequestStore {
const currentUser = data.current_user;
- this.cherryPickInForkPath = currentUser.cherry_pick_in_fork_path;
- this.revertInForkPath = currentUser.revert_in_fork_path;
-
- this.canRemoveSourceBranch = currentUser.can_remove_source_branch || false;
- this.canCreateIssue = currentUser.can_create_issue || false;
- this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false;
- this.canRevertInCurrentMR = currentUser.can_revert_on_current_merge_request || false;
+ if (currentUser) {
+ this.cherryPickInForkPath = currentUser.cherry_pick_in_fork_path;
+ this.revertInForkPath = currentUser.revert_in_fork_path;
+
+ this.canRemoveSourceBranch = currentUser.can_remove_source_branch || false;
+ this.canCreateIssue = currentUser.can_create_issue || false;
+ this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false;
+ this.canRevertInCurrentMR = currentUser.can_revert_on_current_merge_request || false;
+ }
this.setState(data);
}
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 3892d9dbd07..1c9bfe962f6 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -98,6 +98,14 @@
}
}
+.refresh-dashboard-button {
+ margin-top: 22px;
+
+ @media(max-width: map-get($grid-breakpoints, sm)) {
+ margin-top: 0;
+ }
+}
+
.metric-area {
opacity: 0.25;
}
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 2b7571e42b7..c9f46eb72c5 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -117,6 +117,7 @@ class ProfilesController < Profiles::ApplicationController
:private_profile,
:include_private_contributions,
:timezone,
+ :job_title,
status: [:emoji, :message]
)
end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index c4cc1adcd4e..aac6ecb07e4 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -66,7 +66,7 @@ module Projects
[
:runners_token, :builds_enabled, :build_allow_git_fetch,
:build_timeout_human_readable, :build_coverage_regex, :public_builds,
- :auto_cancel_pending_pipelines, :ci_config_path,
+ :auto_cancel_pending_pipelines, :forward_deployment_enabled, :ci_config_path,
auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy],
ci_cd_settings_attributes: [:default_git_depth]
].tap do |list|
diff --git a/app/models/appearance.rb b/app/models/appearance.rb
index 1104b676bc4..9da4dfd43b5 100644
--- a/app/models/appearance.rb
+++ b/app/models/appearance.rb
@@ -38,7 +38,7 @@ class Appearance < ApplicationRecord
def single_appearance_row
if self.class.any?
- errors.add(:single_appearance_row, 'Only 1 appearances row can exist')
+ errors.add(:base, _('Only 1 appearances row can exist'))
end
end
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index a9856203cc0..98b8981754f 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -389,7 +389,7 @@ module ApplicationSettingImplementation
def terms_exist
return unless enforce_terms?
- errors.add(:terms, "You need to set terms to be enforced") unless terms.present?
+ errors.add(:base, _('You need to set terms to be enforced')) unless terms.present?
end
def expire_performance_bar_allowed_user_ids_cache
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 8defe742ec4..f9a5f713814 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -148,7 +148,7 @@ module Ci
def valid_file_format?
unless TYPE_AND_FORMAT_PAIRS[self.file_type&.to_sym] == self.file_format&.to_sym
- errors.add(:file_format, 'Invalid file format with specified file type')
+ errors.add(:base, _('Invalid file format with specified file type'))
end
end
diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb
index bdd7ad90fba..233920f4fe2 100644
--- a/app/models/clusters/applications/ingress.rb
+++ b/app/models/clusters/applications/ingress.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Ingress < ApplicationRecord
- VERSION = '1.29.3'
+ VERSION = '1.29.7'
MODSECURITY_LOG_CONTAINER_NAME = 'modsecurity-log'
self.table_name = 'clusters_applications_ingress'
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 6e890de924e..7f155a8d435 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -306,7 +306,7 @@ module Clusters
.where.not(id: id)
if duplicate_management_clusters.any?
- errors.add(:environment_scope, "cannot add duplicated environment scope")
+ errors.add(:environment_scope, 'cannot add duplicated environment scope')
end
end
@@ -380,7 +380,7 @@ module Clusters
def restrict_modification
if provider&.on_creation?
- errors.add(:base, "cannot modify during creation")
+ errors.add(:base, _('Cannot modify provider during creation'))
return false
end
diff --git a/app/models/concerns/has_repository.rb b/app/models/concerns/has_repository.rb
index d04a6408a21..0887236e65e 100644
--- a/app/models/concerns/has_repository.rb
+++ b/app/models/concerns/has_repository.rb
@@ -19,7 +19,7 @@ module HasRepository
def valid_repo?
repository.exists?
rescue
- errors.add(:path, _('Invalid repository path'))
+ errors.add(:base, _('Invalid repository path'))
false
end
diff --git a/app/models/concerns/milestoneable.rb b/app/models/concerns/milestoneable.rb
index 7df6981a129..3ffb32f94fc 100644
--- a/app/models/concerns/milestoneable.rb
+++ b/app/models/concerns/milestoneable.rb
@@ -37,7 +37,7 @@ module Milestoneable
private
def milestone_is_valid
- errors.add(:milestone_id, message: "is invalid") if respond_to?(:milestone_id) && milestone_id.present? && !milestone_available?
+ errors.add(:milestone_id, 'is invalid') if respond_to?(:milestone_id) && milestone_id.present? && !milestone_available?
end
end
diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb
index f61a0bbc65b..dddf96837b7 100644
--- a/app/models/concerns/time_trackable.rb
+++ b/app/models/concerns/time_trackable.rb
@@ -77,7 +77,7 @@ module TimeTrackable
return if time_spent.nil? || time_spent == :reset
if time_spent < 0 && (time_spent.abs > original_total_time_spent)
- errors.add(:time_spent, 'Time to subtract exceeds the total time spent')
+ errors.add(:base, _('Time to subtract exceeds the total time spent'))
end
end
diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb
index 31c813edb67..a9844f627b7 100644
--- a/app/models/deploy_token.rb
+++ b/app/models/deploy_token.rb
@@ -105,7 +105,7 @@ class DeployToken < ApplicationRecord
end
def ensure_at_least_one_scope
- errors.add(:base, "Scopes can't be blank") unless read_repository || read_registry
+ errors.add(:base, _("Scopes can't be blank")) unless read_repository || read_registry
end
def default_username
diff --git a/app/models/description_version.rb b/app/models/description_version.rb
index 05362a2f90b..f69564f4893 100644
--- a/app/models/description_version.rb
+++ b/app/models/description_version.rb
@@ -19,7 +19,13 @@ class DescriptionVersion < ApplicationRecord
def exactly_one_issuable
issuable_count = self.class.issuable_attrs.count { |attr| self["#{attr}_id"] }
- errors.add(:base, "Exactly one of #{self.class.issuable_attrs.join(', ')} is required") if issuable_count != 1
+ if issuable_count != 1
+ errors.add(
+ :base,
+ _("Exactly one of %{attributes} is required") %
+ { attributes: self.class.issuable_attrs.join(', ') }
+ )
+ end
end
end
diff --git a/app/models/external_pull_request.rb b/app/models/external_pull_request.rb
index 65ae8d95500..9c6d05f773a 100644
--- a/app/models/external_pull_request.rb
+++ b/app/models/external_pull_request.rb
@@ -78,7 +78,7 @@ class ExternalPullRequest < ApplicationRecord
def not_from_fork
if from_fork?
- errors.add(:base, 'Pull requests from fork are not supported')
+ errors.add(:base, _('Pull requests from fork are not supported'))
end
end
diff --git a/app/models/milestone_release.rb b/app/models/milestone_release.rb
index 713c8ef7b94..0a6165c8254 100644
--- a/app/models/milestone_release.rb
+++ b/app/models/milestone_release.rb
@@ -11,6 +11,6 @@ class MilestoneRelease < ApplicationRecord
def same_project_between_milestone_and_release
return if milestone&.project_id == release&.project_id
- errors.add(:base, 'does not have the same project as the milestone')
+ errors.add(:base, _('Release does not have the same project as the milestone'))
end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index fbc010c6b7c..631bd930e2f 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -376,7 +376,7 @@ class Namespace < ApplicationRecord
def nesting_level_allowed
if ancestors.count > Group::NUMBER_OF_ANCESTORS_ALLOWED
- errors.add(:parent_id, "has too deep level of nesting")
+ errors.add(:parent_id, 'has too deep level of nesting')
end
end
diff --git a/app/models/project_ci_cd_setting.rb b/app/models/project_ci_cd_setting.rb
index b26a3025b61..39e177e8bd8 100644
--- a/app/models/project_ci_cd_setting.rb
+++ b/app/models/project_ci_cd_setting.rb
@@ -31,7 +31,7 @@ class ProjectCiCdSetting < ApplicationRecord
end
def forward_deployment_enabled?
- super && ::Feature.enabled?(:forward_deployment_enabled, project)
+ super && ::Feature.enabled?(:forward_deployment_enabled, project, default_enabled: true)
end
private
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index 7cdbb124dee..2bf14a6ed25 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -168,7 +168,7 @@ class IssueTrackerService < Service
return if project.blank?
if project.services.external_issue_trackers.where.not(id: id).any?
- errors.add(:base, 'Another issue tracker is already in use. Only one issue tracker service can be active at a time')
+ errors.add(:base, _('Another issue tracker is already in use. Only one issue tracker service can be active at a time'))
end
end
end
diff --git a/app/models/prometheus_alert.rb b/app/models/prometheus_alert.rb
index 1014231102f..1dc7dc73e31 100644
--- a/app/models/prometheus_alert.rb
+++ b/app/models/prometheus_alert.rb
@@ -69,13 +69,13 @@ class PrometheusAlert < ApplicationRecord
def require_valid_environment_project!
return if project == environment&.project
- errors.add(:environment, "invalid project")
+ errors.add(:environment, 'invalid project')
end
def require_valid_metric_project!
return if prometheus_metric&.common?
return if project == prometheus_metric&.project
- errors.add(:prometheus_metric, "invalid project")
+ errors.add(:prometheus_metric, 'invalid project')
end
end
diff --git a/app/models/resource_event.rb b/app/models/resource_event.rb
index 9b3a211ad43..2c0052b0be3 100644
--- a/app/models/resource_event.rb
+++ b/app/models/resource_event.rb
@@ -37,6 +37,9 @@ class ResourceEvent < ApplicationRecord
return true if issuable_count == 1
end
- errors.add(:base, "Exactly one of #{self.class.issuable_attrs.join(', ')} is required")
+ errors.add(
+ :base, _("Exactly one of %{attributes} is required") %
+ { attributes: self.class.issuable_attrs.join(', ') }
+ )
end
end
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index 0427d5b9ca7..f3a9293376f 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -111,7 +111,10 @@ class SentNotification < ApplicationRecord
note = create_reply('Test', dryrun: true)
unless note.valid?
- self.errors.add(:base, "Note parameters are invalid: #{note.errors.full_messages.to_sentence}")
+ self.errors.add(
+ :base, _("Note parameters are invalid: %{errors}") %
+ { errors: note.errors.full_messages.to_sentence }
+ )
end
end
diff --git a/app/models/timelog.rb b/app/models/timelog.rb
index 4ddaf6bcb86..f52dd74d4c9 100644
--- a/app/models/timelog.rb
+++ b/app/models/timelog.rb
@@ -28,9 +28,9 @@ class Timelog < ApplicationRecord
def issuable_id_is_present
if issue_id && merge_request_id
- errors.add(:base, 'Only Issue ID or Merge Request ID is required')
+ errors.add(:base, _('Only Issue ID or Merge Request ID is required'))
elsif issuable.nil?
- errors.add(:base, 'Issue or Merge Request ID is required')
+ errors.add(:base, _('Issue or Merge Request ID is required'))
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index f3db0522edc..81cabc67c3b 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -162,6 +162,7 @@ class User < ApplicationRecord
has_one :status, class_name: 'UserStatus'
has_one :user_preference
+ has_one :user_detail
#
# Validations
@@ -259,8 +260,10 @@ class User < ApplicationRecord
delegate :sourcegraph_enabled, :sourcegraph_enabled=, to: :user_preference
delegate :setup_for_company, :setup_for_company=, to: :user_preference
delegate :render_whitespace_in_code, :render_whitespace_in_code=, to: :user_preference
+ delegate :job_title, :job_title=, to: :user_detail, allow_nil: true
accepts_nested_attributes_for :user_preference, update_only: true
+ accepts_nested_attributes_for :user_detail, update_only: true
state_machine :state, initial: :active do
event :block do
@@ -1619,6 +1622,10 @@ class User < ApplicationRecord
super.presence || build_user_preference
end
+ def user_detail
+ super.presence || build_user_detail
+ end
+
def todos_limited_to(ids)
todos.where(id: ids)
end
diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb
new file mode 100644
index 00000000000..1621f336111
--- /dev/null
+++ b/app/models/user_detail.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class UserDetail < ApplicationRecord
+ belongs_to :user
+
+ validates :job_title, presence: true, length: { maximum: 200 }
+end
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index a72179f40ad..e6be364b48f 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -88,6 +88,15 @@
= _("New pipelines will cancel older, pending pipelines on the same branch")
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'auto-cancel-pending-pipelines'), target: '_blank'
+ .form-group
+ .form-check
+ = f.check_box :forward_deployment_enabled, { class: 'form-check-input' }
+ = f.label :forward_deployment_enabled, class: 'form-check-label' do
+ %strong= _("Skip older, pending deployment jobs")
+ .form-text.text-muted
+ = _("When a deployment job is successful, skip older deployment jobs that are still pending")
+ = link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'skip-older-pending-deployment-jobs'), target: '_blank'
+
%hr
.form-group
= f.label :build_coverage_regex, _("Test coverage parsing"), class: 'label-bold'