Skip to content
Snippets Groups Projects
Commit 40254b9a authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 22a0d312
No related branches found
No related tags found
No related merge requests found
Showing
with 257 additions and 42 deletions
Loading
Loading
@@ -28,29 +28,38 @@ export default {
{
key: 'error',
label: __('Error'),
thClass: 'w-70p',
tdClass: 'table-col d-flex align-items-center d-sm-table-cell',
thClass: 'w-60p',
tdClass: 'table-col d-flex d-sm-table-cell px-3',
},
{
key: 'events',
label: __('Events'),
tdClass: 'table-col d-flex align-items-center d-sm-table-cell',
thClass: 'text-right',
tdClass: 'table-col d-flex d-sm-table-cell',
},
{
key: 'users',
label: __('Users'),
tdClass: 'table-col d-flex align-items-center d-sm-table-cell',
thClass: 'text-right',
tdClass: 'table-col d-flex d-sm-table-cell',
},
{
key: 'lastSeen',
label: __('Last seen'),
thClass: 'w-15p',
tdClass: 'table-col d-flex align-items-center d-sm-table-cell',
thClass: '',
tdClass: 'table-col d-flex d-sm-table-cell',
},
{
key: 'ignore',
label: '',
tdClass: 'table-col d-flex align-items-center d-sm-table-cell',
thClass: 'w-3rem',
tdClass: 'table-col d-flex pl-0 d-sm-table-cell',
},
{
key: 'resolved',
label: '',
thClass: 'w-3rem',
tdClass: 'table-col d-flex pl-0 d-sm-table-cell',
},
{
key: 'details',
Loading
Loading
@@ -197,9 +206,7 @@ export default {
<template>
<div class="error-list">
<div v-if="errorTrackingEnabled">
<div
class="row flex-column flex-sm-row align-items-sm-center row-top m-0 mt-sm-2 mx-sm-1 p-0 p-sm-3"
>
<div class="row flex-column flex-sm-row align-items-sm-center row-top m-0 mt-sm-2 p-0 p-sm-3">
<div class="search-box flex-fill mr-sm-2 my-3 m-sm-0 p-3 p-sm-0">
<div class="filtered-search-box mb-0">
<gl-dropdown
Loading
Loading
@@ -333,6 +340,16 @@ export default {
<gl-icon name="eye-slash" :size="12" />
</gl-button>
</template>
<template v-slot:resolved="errors">
<gl-button
ref="resolveError"
v-gl-tooltip
:title="__('Resolve')"
@click="updateIssueStatus(errors.item.id, 'resolved')"
>
<gl-icon name="check-circle" :size="12" />
</gl-button>
</template>
<template v-slot:details="errors">
<gl-button
:href="getDetailsLink(errors.item.id)"
Loading
Loading
Loading
Loading
@@ -21,12 +21,17 @@ export const addDelimiter = text =>
export const highCountTrim = count => (count > 99 ? '99+' : count);
 
/**
* Converts first char to uppercase and replaces undercores with spaces
* @param {String} string
* Converts first char to uppercase and replaces the given separator with spaces
* @param {String} string - The string to humanize
* @param {String} separator - The separator used to separate words (defaults to "_")
* @requires {String}
* @returns {String}
*/
export const humanize = string =>
string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1);
export const humanize = (string, separator = '_') => {
const replaceRegex = new RegExp(separator, 'g');
return string.charAt(0).toUpperCase() + string.replace(replaceRegex, ' ').slice(1);
};
 
/**
* Replaces underscores with dashes
Loading
Loading
@@ -45,7 +50,11 @@ export const slugify = (str, separator = '-') => {
const slug = str
.trim()
.toLowerCase()
.replace(/[^a-zA-Z0-9_.-]+/g, separator);
.replace(/[^a-zA-Z0-9_.-]+/g, separator)
// Remove any duplicate separators or separator prefixes/suffixes
.split(separator)
.filter(Boolean)
.join(separator);
 
return slug === separator ? '' : slug;
};
Loading
Loading
@@ -159,6 +168,15 @@ export const convertToSentenceCase = string => {
return splitWord.join(' ');
};
 
/**
* Converts a sentence to title case
* e.g. Hello world => Hello World
*
* @param {String} string
* @returns {String}
*/
export const convertToTitleCase = string => string.replace(/\b[a-z]/g, s => s.toUpperCase());
/**
* Splits camelCase or PascalCase words
* e.g. HelloWorld => Hello World
Loading
Loading
import $ from 'jquery';
import { convertToTitleCase, humanize, slugify } from '../lib/utils/text_utility';
import { getParameterValues } from '../lib/utils/url_utility';
import projectNew from './project_new';
 
const prepareParameters = () => {
const name = getParameterValues('name')[0];
const path = getParameterValues('path')[0];
// If the name param exists but the path doesn't then generate it from the name
if (name && !path) {
return { name, path: slugify(name) };
}
// If the path param exists but the name doesn't then generate it from the path
if (path && !name) {
return { name: convertToTitleCase(humanize(path, '-')), path };
}
return { name, path };
};
export default () => {
const pathParam = getParameterValues('path')[0];
const nameParam = getParameterValues('name')[0];
const $projectPath = $('.js-path-name');
let hasUserDefinedProjectName = false;
const $projectName = $('.js-project-name');
// get the path url and append it in the input
$projectPath.val(pathParam);
const $projectPath = $('.js-path-name');
const { name, path } = prepareParameters();
 
// get the project name from the URL and set it as input value
$projectName.val(nameParam);
$projectName.val(name);
// get the path url and append it in the input
$projectPath.val(path);
 
// generate slug when project name changes
$projectName.keyup(() => projectNew.onProjectNameChange($projectName, $projectPath));
$projectName.on('keyup', () => {
projectNew.onProjectNameChange($projectName, $projectPath);
hasUserDefinedProjectName = $projectName.val().trim().length > 0;
});
// generate project name from the slug if one isn't set
$projectPath.on('keyup', () =>
projectNew.onProjectPathChange($projectName, $projectPath, hasUserDefinedProjectName),
);
};
import $ from 'jquery';
import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
import { slugify } from '../lib/utils/text_utility';
import { convertToTitleCase, humanize, slugify } from '../lib/utils/text_utility';
import { s__ } from '~/locale';
 
let hasUserDefinedProjectPath = false;
let hasUserDefinedProjectName = false;
const onProjectNameChange = ($projectNameInput, $projectPathInput) => {
const slug = slugify($projectNameInput.val());
$projectPathInput.val(slug);
};
const onProjectPathChange = ($projectNameInput, $projectPathInput, hasExistingProjectName) => {
const slug = $projectPathInput.val();
if (!hasExistingProjectName) {
$projectNameInput.val(convertToTitleCase(humanize(slug, '[-_]')));
}
};
const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => {
$projectNameInput.off('keyup change').on('keyup change', () => {
onProjectNameChange($projectNameInput, $projectPathInput);
hasUserDefinedProjectName = $projectNameInput.val().trim().length > 0;
hasUserDefinedProjectPath = $projectPathInput.val().trim().length > 0;
});
$projectPathInput.off('keyup change').on('keyup change', () => {
onProjectPathChange($projectNameInput, $projectPathInput, hasUserDefinedProjectName);
hasUserDefinedProjectPath = $projectPathInput.val().trim().length > 0;
});
};
 
const deriveProjectPathFromUrl = $projectImportUrl => {
const $currentProjectName = $projectImportUrl
.parents('.toggle-import-form')
.find('#project_name');
const $currentProjectPath = $projectImportUrl
.parents('.toggle-import-form')
.find('#project_path');
if (hasUserDefinedProjectPath) {
return;
}
Loading
Loading
@@ -30,14 +61,10 @@ const deriveProjectPathFromUrl = $projectImportUrl => {
const pathMatch = /\/([^/]+)$/.exec(importUrl);
if (pathMatch) {
$currentProjectPath.val(pathMatch[1]);
onProjectPathChange($currentProjectName, $currentProjectPath, false);
}
};
 
const onProjectNameChange = ($projectNameInput, $projectPathInput) => {
const slug = slugify($projectNameInput.val());
$projectPathInput.val(slug);
};
const bindEvents = () => {
const $newProjectForm = $('#new_project');
const $projectImportUrl = $('#project_import_url');
Loading
Loading
@@ -202,10 +229,7 @@ const bindEvents = () => {
const $activeTabProjectName = $('.tab-pane.active #project_name');
const $activeTabProjectPath = $('.tab-pane.active #project_path');
$activeTabProjectName.focus();
$activeTabProjectName.keyup(() => {
onProjectNameChange($activeTabProjectName, $activeTabProjectPath);
hasUserDefinedProjectPath = $activeTabProjectPath.val().trim().length > 0;
});
setProjectNamePathHandlers($activeTabProjectName, $activeTabProjectPath);
}
 
$useTemplateBtn.on('change', chooseTemplate);
Loading
Loading
@@ -220,26 +244,24 @@ const bindEvents = () => {
$projectPath.val($projectPath.val().trim());
});
 
$projectPath.on('keyup', () => {
hasUserDefinedProjectPath = $projectPath.val().trim().length > 0;
});
$projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl));
 
$('.js-import-git-toggle-button').on('click', () => {
const $projectMirror = $('#project_mirror');
 
$projectMirror.attr('disabled', !$projectMirror.attr('disabled'));
setProjectNamePathHandlers(
$('.tab-pane.active #project_name'),
$('.tab-pane.active #project_path'),
);
});
 
$projectName.on('keyup change', () => {
onProjectNameChange($projectName, $projectPath);
hasUserDefinedProjectPath = $projectPath.val().trim().length > 0;
});
setProjectNamePathHandlers($projectName, $projectPath);
};
 
export default {
bindEvents,
deriveProjectPathFromUrl,
onProjectNameChange,
onProjectPathChange,
};
Loading
Loading
@@ -461,6 +461,7 @@ img.emoji {
.w-3rem { width: 3rem; }
.w-15p { width: 15%; }
.w-30p { width: 30%; }
.w-60p { width: 60%; }
.w-70p { width: 70%; }
 
.h-12em { height: 12em; }
Loading
Loading
Loading
Loading
@@ -17,6 +17,8 @@ class DeploymentsFinder
def execute
items = init_collection
items = by_updated_at(items)
items = by_environment(items)
items = by_status(items)
sort(items)
end
 
Loading
Loading
@@ -58,6 +60,24 @@ class DeploymentsFinder
items
end
 
def by_environment(items)
if params[:environment].present?
items.for_environment_name(params[:environment])
else
items
end
end
def by_status(items)
return items unless params[:status].present?
unless Deployment.statuses.key?(params[:status])
raise ArgumentError, "The deployment status #{params[:status]} is invalid"
end
items.for_status(params[:status])
end
def sort_params
order_by = ALLOWED_SORT_VALUES.include?(params[:order_by]) ? params[:order_by] : DEFAULT_SORT_VALUE
order_direction = ALLOWED_SORT_DIRECTIONS.include?(params[:sort]) ? params[:sort] : DEFAULT_SORT_DIRECTION
Loading
Loading
Loading
Loading
@@ -30,6 +30,11 @@ class Deployment < ApplicationRecord
delegate :name, to: :environment, prefix: true
 
scope :for_environment, -> (environment) { where(environment_id: environment) }
scope :for_environment_name, -> (name) do
joins(:environment).where(environments: { name: name })
end
scope :for_status, -> (status) { where(status: status) }
 
scope :visible, -> { where(status: %i[running success failed canceled]) }
 
Loading
Loading
Loading
Loading
@@ -57,6 +57,8 @@ class Group < Namespace
 
has_one :import_export_upload
 
has_many :import_failures, inverse_of: :group
accepts_nested_attributes_for :variables, allow_destroy: true
 
validate :visibility_level_allowed_by_projects
Loading
Loading
Loading
Loading
@@ -2,6 +2,8 @@
 
class ImportFailure < ApplicationRecord
belongs_to :project
belongs_to :group
 
validates :project, presence: true
validates :project, presence: true, unless: :group
validates :group, presence: true, unless: :project
end
---
title: 'Resolve Create new project: Auto-populate project slug string to project name
if name is empty'
merge_request: 22627
author:
type: changed
---
title: Migrate issue trackers data
merge_request: 18639
author:
type: other
---
title: Hide mirror admin actions from developers
merge_request: 21569
author:
type: fixed
---
title: Add retry logic for failures during import
merge_request: 22265
author:
type: added
---
title: Filter deployments using the environment & status
merge_request: 22996
author:
type: added
---
title: Resolve Sentry errors from error tracking list
merge_request: 23135
author:
type: added
---
title: Fix analytics tracking for new merge request notes
merge_request: 23273
author:
type: fixed
# frozen_string_literal: true
Retriable.configure do |config|
config.contexts[:relation_import] = {
tries: ENV.fetch('RELATION_IMPORT_TRIES', 3).to_i,
base_interval: ENV.fetch('RELATION_IMPORT_BASE_INTERVAL', 0.5).to_f,
multiplier: ENV.fetch('RELATION_IMPORT_MULTIPLIER', 1.5).to_f,
rand_factor: ENV.fetch('RELATION_IMPORT_RAND_FACTOR', 0.5).to_f,
on: Gitlab::ImportExport::ImportFailureService::RETRIABLE_EXCEPTIONS
}
end
# frozen_string_literal: true
class AddRetryCountAndGroupIdToImportFailures < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :import_failures, :retry_count, :integer
add_column :import_failures, :group_id, :integer
change_column_null(:import_failures, :project_id, true)
end
end
# frozen_string_literal: true
class AddGroupIndexAndFkToImportFailures < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
GROUP_INDEX = 'index_import_failures_on_group_id_not_null'.freeze
disable_ddl_transaction!
def up
add_concurrent_index(:import_failures, :group_id, where: 'group_id IS NOT NULL', name: GROUP_INDEX)
add_concurrent_foreign_key(:import_failures, :namespaces, column: :group_id)
end
def down
remove_foreign_key(:import_failures, column: :group_id)
remove_concurrent_index_by_name(:import_failures, GROUP_INDEX)
end
end
# frozen_string_literal: true
class UpdateProjectIndexToImportFailures < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
PROJECT_INDEX_OLD = 'index_import_failures_on_project_id'.freeze
PROJECT_INDEX_NEW = 'index_import_failures_on_project_id_not_null'.freeze
disable_ddl_transaction!
def up
add_concurrent_index(:import_failures, :project_id, where: 'project_id IS NOT NULL', name: PROJECT_INDEX_NEW)
remove_concurrent_index_by_name(:import_failures, PROJECT_INDEX_OLD)
end
def down
add_concurrent_index(:import_failures, :project_id, name: PROJECT_INDEX_OLD)
remove_concurrent_index_by_name(:import_failures, PROJECT_INDEX_NEW)
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment