summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile6
-rw-r--r--Gemfile.lock21
-rw-r--r--app/assets/javascripts/gl_dropdown.js116
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/memory_graph.vue129
-rw-r--r--app/assets/stylesheets/framework/memory_graph.scss18
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss1
-rw-r--r--app/controllers/projects/error_tracking_controller.rb2
-rw-r--r--app/graphql/mutations/todos/restore.rb36
-rw-r--r--app/graphql/types/mutation_type.rb1
-rw-r--r--app/models/clusters/applications/knative.rb10
-rw-r--r--app/models/error_tracking/project_error_tracking_setting.rb3
-rw-r--r--app/services/error_tracking/list_issues_service.rb12
-rw-r--r--changelogs/unreleased/31391-update-sparkline-chart-deployment-widget.yml5
-rw-r--r--changelogs/unreleased/31914-graphql-todo-restore-pd.yml5
-rw-r--r--changelogs/unreleased/34068-sort-by-first-seen-last-seen-and-frequency-in-sentry-error-list-in-.yml5
-rw-r--r--changelogs/unreleased/34636-drop-deprecated-column-from-projects-table.yml5
-rw-r--r--changelogs/unreleased/Remove-var-from-gl_dropdown-js.yml5
-rw-r--r--config/initializers/hamlit.rb16
-rw-r--r--config/knative/api_groups.yml11
-rw-r--r--config/knative/api_resources.yml70
-rw-r--r--db/fixtures/development/01_admin.rb2
-rw-r--r--db/fixtures/development/03_project.rb2
-rw-r--r--db/fixtures/development/06_teams.rb2
-rw-r--r--db/fixtures/development/07_milestones.rb2
-rw-r--r--db/fixtures/development/09_issues.rb2
-rw-r--r--db/fixtures/development/10_merge_requests.rb2
-rw-r--r--db/fixtures/development/11_keys.rb2
-rw-r--r--db/fixtures/development/12_snippets.rb2
-rw-r--r--db/fixtures/development/13_comments.rb2
-rw-r--r--db/fixtures/development/14_pipelines.rb2
-rw-r--r--db/fixtures/development/15_award_emoji.rb2
-rw-r--r--db/fixtures/development/16_protected_branches.rb2
-rw-r--r--db/fixtures/development/17_cycle_analytics.rb2
-rw-r--r--db/fixtures/development/19_environments.rb2
-rw-r--r--db/fixtures/development/20_nested_groups.rb2
-rw-r--r--db/fixtures/development/24_forks.rb2
-rw-r--r--db/fixtures/development/25_api_personal_access_token.rb2
-rw-r--r--db/post_migrate/20191015154408_drop_merge_requests_require_code_owner_approval_from_projects.rb27
-rw-r--r--db/schema.rb2
-rw-r--r--doc/administration/auth/smartcard.md4
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql36
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json133
-rw-r--r--doc/api/graphql/reference/index.md8
-rw-r--r--doc/api/merge_requests.md2
-rw-r--r--doc/development/packages.md31
-rw-r--r--doc/user/application_security/dependency_scanning/index.md34
-rw-r--r--doc/user/application_security/sast/index.md16
-rw-r--r--doc/user/packages/index.md6
-rw-r--r--doc/user/packages/npm_registry/index.md7
-rw-r--r--doc/user/project/quick_actions.md4
-rw-r--r--lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml2
-rw-r--r--lib/gitlab/kubernetes/kubectl_cmd.rb10
-rw-r--r--lib/sentry/client.rb44
-rw-r--r--locale/gitlab.pot6
-rw-r--r--qa/qa/resource/deploy_token.rb8
-rw-r--r--qa/qa/resource/file.rb10
-rw-r--r--qa/qa/resource/fork.rb4
-rw-r--r--qa/qa/resource/kubernetes_cluster.rb36
-rw-r--r--qa/qa/resource/label.rb10
-rw-r--r--qa/qa/resource/merge_request.rb16
-rw-r--r--qa/qa/resource/personal_access_token.rb8
-rw-r--r--qa/qa/resource/project.rb22
-rw-r--r--qa/qa/resource/project_imported_from_github.rb16
-rw-r--r--qa/qa/resource/project_milestone.rb6
-rw-r--r--qa/qa/resource/snippet.rb14
-rw-r--r--qa/qa/resource/ssh_key.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/group/create_group_with_mattermost_team_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb10
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb8
-rwxr-xr-xscripts/review_apps/automated_cleanup.rb4
-rw-r--r--spec/controllers/projects/error_tracking_controller_spec.rb25
-rw-r--r--spec/frontend/environments/environment_rollback_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap15
-rw-r--r--spec/frontend/vue_shared/components/memory_graph_spec.js53
-rw-r--r--spec/graphql/mutations/todos/restore_spec.rb66
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js1
-rw-r--r--spec/javascripts/vue_shared/components/memory_graph_spec.js131
-rw-r--r--spec/javascripts/vue_shared/components/mock_data.js67
-rw-r--r--spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb16
-rw-r--r--spec/lib/sentry/client_spec.rb37
-rw-r--r--spec/migrations/20191015154408_drop_merge_requests_require_code_owner_approval_from_projects_spec.rb56
-rw-r--r--spec/models/clusters/applications/knative_spec.rb7
-rw-r--r--spec/requests/api/graphql/mutations/todos/restore_spec.rb97
-rw-r--r--spec/services/error_tracking/list_issues_service_spec.rb5
-rw-r--r--spec/support/sidekiq.rb26
-rw-r--r--spec/support/sidekiq_middleware.rb27
92 files changed, 1006 insertions, 724 deletions
diff --git a/Gemfile b/Gemfile
index 0d7ccb0e455..65fd4f81744 100644
--- a/Gemfile
+++ b/Gemfile
@@ -101,7 +101,7 @@ gem 'hashie-forbidden_attributes'
gem 'kaminari', '~> 1.0'
# HAML
-gem 'hamlit', '~> 2.8.8'
+gem 'hamlit', '~> 2.10.0'
# Files attachments
gem 'carrierwave', '~> 1.3'
@@ -135,7 +135,7 @@ gem 'aws-sdk'
gem 'faraday_middleware-aws-signers-v4'
# Markdown and HTML processing
-gem 'html-pipeline', '~> 2.8'
+gem 'html-pipeline', '~> 2.12'
gem 'deckar01-task_list', '2.2.1'
gem 'gitlab-markup', '~> 1.7.0'
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
@@ -373,7 +373,7 @@ group :development, :test do
gem 'rubocop-rspec', '~> 1.22.1'
gem 'scss_lint', '~> 0.56.0', require: false
- gem 'haml_lint', '~> 0.31.0', require: false
+ gem 'haml_lint', '~> 0.34.0', require: false
gem 'simplecov', '~> 0.16.1', require: false
gem 'bundler-audit', '~> 0.5.0', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index a568e0c59e0..4563c2d53c0 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -460,17 +460,16 @@ GEM
guard (~> 2.1)
guard-compat (~> 1.1)
rspec (>= 2.99.0, < 4.0)
- haml (5.0.4)
+ haml (5.1.2)
temple (>= 0.8.0)
tilt
- haml_lint (0.31.0)
- haml (>= 4.0, < 5.1)
+ haml_lint (0.34.0)
+ haml (>= 4.0, < 5.2)
rainbow
- rake (>= 10, < 13)
rubocop (>= 0.50.0)
sysexits (~> 1.1)
- hamlit (2.8.8)
- temple (>= 0.8.0)
+ hamlit (2.10.0)
+ temple (>= 0.8.2)
thor
tilt
hangouts-chat (0.0.5)
@@ -484,7 +483,7 @@ GEM
hipchat (1.5.2)
httparty
mimemagic
- html-pipeline (2.8.4)
+ html-pipeline (2.12.2)
activesupport (>= 2)
nokogiri (>= 1.4)
html2text (0.2.0)
@@ -1017,7 +1016,7 @@ GEM
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
- temple (0.8.1)
+ temple (0.8.2)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
test-prof (0.10.0)
@@ -1219,13 +1218,13 @@ DEPENDENCIES
grpc (~> 1.24.0)
gssapi
guard-rspec
- haml_lint (~> 0.31.0)
- hamlit (~> 2.8.8)
+ haml_lint (~> 0.34.0)
+ hamlit (~> 2.10.0)
hangouts-chat (~> 0.0.5)
hashie-forbidden_attributes
health_check (~> 2.6.0)
hipchat (~> 1.5.0)
- html-pipeline (~> 2.8)
+ html-pipeline (~> 2.12)
html2text
httparty (~> 0.16.4)
icalendar
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 5abf12c5d8c..65d05887453 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, vars-on-top, no-cond-assign, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, no-param-reassign, no-loop-func */
+/* eslint-disable func-names, no-underscore-dangle, one-var, no-cond-assign, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, no-param-reassign, no-loop-func */
import $ from 'jquery';
import _ from 'underscore';
@@ -33,13 +33,12 @@ const FILTER_INPUT = '.dropdown-input .dropdown-input-field:not(.dropdown-no-fil
const NO_FILTER_INPUT = '.dropdown-input .dropdown-input-field.dropdown-no-filter';
function GitLabDropdownInput(input, options) {
- var $inputContainer, $clearButton;
- var _this = this;
+ const _this = this;
this.input = input;
this.options = options;
this.fieldName = this.options.fieldName || 'field-name';
- $inputContainer = this.input.parent();
- $clearButton = $inputContainer.find('.js-dropdown-input-clear');
+ const $inputContainer = this.input.parent();
+ const $clearButton = $inputContainer.find('.js-dropdown-input-clear');
$clearButton.on('click', e => {
// Clear click
e.preventDefault();
@@ -52,13 +51,13 @@ function GitLabDropdownInput(input, options) {
this.input
.on('keydown', e => {
- var keyCode = e.which;
+ const keyCode = e.which;
if (keyCode === 13 && !options.elIsInput) {
e.preventDefault();
}
})
.on('input', e => {
- var val = e.currentTarget.value || _this.options.inputFieldName;
+ let val = e.currentTarget.value || _this.options.inputFieldName;
val = val
.split(' ')
.join('-') // replaces space with dash
@@ -78,12 +77,12 @@ GitLabDropdownInput.prototype.onInput = function(cb) {
};
function GitLabDropdownFilter(input, options) {
- var $clearButton, $inputContainer, ref, timeout;
+ let ref, timeout;
this.input = input;
this.options = options;
this.filterInputBlur = (ref = this.options.filterInputBlur) != null ? ref : true;
- $inputContainer = this.input.parent();
- $clearButton = $inputContainer.find('.js-dropdown-input-clear');
+ const $inputContainer = this.input.parent();
+ const $clearButton = $inputContainer.find('.js-dropdown-input-clear');
$clearButton.on('click', e => {
// Clear click
e.preventDefault();
@@ -97,7 +96,7 @@ function GitLabDropdownFilter(input, options) {
timeout = '';
this.input
.on('keydown', e => {
- var keyCode = e.which;
+ const keyCode = e.which;
if (keyCode === 13 && !options.elIsInput) {
e.preventDefault();
}
@@ -130,11 +129,11 @@ GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) {
};
GitLabDropdownFilter.prototype.filter = function(search_text) {
- var data, elements, group, key, results, tmp;
+ let elements, group, key, results, tmp;
if (this.options.onFilter) {
this.options.onFilter(search_text);
}
- data = this.options.data();
+ const data = this.options.data();
if (data != null && !this.options.filterByText) {
results = data;
if (search_text !== '') {
@@ -178,9 +177,8 @@ GitLabDropdownFilter.prototype.filter = function(search_text) {
elements = this.options.elements();
if (search_text) {
elements.each(function() {
- var $el, matches;
- $el = $(this);
- matches = fuzzaldrinPlus.match($el.text().trim(), search_text);
+ const $el = $(this);
+ const matches = fuzzaldrinPlus.match($el.text().trim(), search_text);
if (!$el.is('.dropdown-header')) {
if (matches.length) {
return $el.show().removeClass('option-hidden');
@@ -238,7 +236,7 @@ GitLabDropdownRemote.prototype.fetchData = function() {
};
function GitLabDropdown(el1, options) {
- var searchFields, selector, self;
+ let selector, self;
this.el = el1;
this.options = options;
this.updateLabel = this.updateLabel.bind(this);
@@ -260,7 +258,7 @@ function GitLabDropdown(el1, options) {
if (_.isString(this.filterInput)) {
this.filterInput = this.getElement(this.filterInput);
}
- searchFields = this.options.search ? this.options.search.fields : [];
+ const searchFields = this.options.search ? this.options.search.fields : [];
if (this.options.data) {
// If we provided data
// data could be an array of objects or a group of arrays
@@ -350,7 +348,7 @@ function GitLabDropdown(el1, options) {
}
});
this.dropdown.on('blur', 'a', e => {
- var $dropdownMenu, $relatedTarget;
+ let $dropdownMenu, $relatedTarget;
if (e.relatedTarget != null) {
$relatedTarget = $(e.relatedTarget);
$dropdownMenu = $relatedTarget.closest('.dropdown-menu');
@@ -372,11 +370,10 @@ function GitLabDropdown(el1, options) {
selector = '.dropdown-page-one .dropdown-content a';
}
this.dropdown.on('click', selector, e => {
- var $el, selected, selectedObj, isMarking;
- $el = $(e.currentTarget);
- selected = self.rowClicked($el);
- selectedObj = selected ? selected[0] : null;
- isMarking = selected ? selected[1] : null;
+ const $el = $(e.currentTarget);
+ const selected = self.rowClicked($el);
+ const selectedObj = selected ? selected[0] : null;
+ const isMarking = selected ? selected[1] : null;
if (this.options.clicked) {
this.options.clicked.call(this, {
selectedObj,
@@ -406,8 +403,7 @@ GitLabDropdown.prototype.toggleLoading = function() {
};
GitLabDropdown.prototype.togglePage = function() {
- var menu;
- menu = $('.dropdown-menu', this.dropdown);
+ const menu = $('.dropdown-menu', this.dropdown);
if (menu.hasClass(PAGE_TWO_CLASS)) {
if (this.remote) {
this.remote.execute();
@@ -419,7 +415,7 @@ GitLabDropdown.prototype.togglePage = function() {
};
GitLabDropdown.prototype.parseData = function(data) {
- var full_html, groupData, html, name;
+ let groupData, html, name;
this.renderedData = data;
if (this.options.filterable && data.length === 0) {
// render no matching results
@@ -447,7 +443,7 @@ GitLabDropdown.prototype.parseData = function(data) {
}
}
// Render the full menu
- full_html = this.renderMenu(html);
+ const full_html = this.renderMenu(html);
return this.appendMenu(full_html);
};
@@ -456,7 +452,7 @@ GitLabDropdown.prototype.renderData = function(data, group) {
};
GitLabDropdown.prototype.shouldPropagate = function(e) {
- var $target;
+ let $target;
if (this.options.multiSelect || this.options.shouldPropagate === false) {
$target = $(e.target);
if (
@@ -487,7 +483,6 @@ GitLabDropdown.prototype.filteredFullData = function() {
};
GitLabDropdown.prototype.opened = function(e) {
- var contentHtml;
this.resetRows();
this.addArrowKeyEvent();
@@ -513,7 +508,7 @@ GitLabDropdown.prototype.opened = function(e) {
);
}
- contentHtml = $('.dropdown-content', this.dropdown).html();
+ const contentHtml = $('.dropdown-content', this.dropdown).html();
if (this.remote && contentHtml === '') {
this.remote.execute();
} else {
@@ -536,7 +531,7 @@ GitLabDropdown.prototype.opened = function(e) {
};
GitLabDropdown.prototype.positionMenuAbove = function() {
- var $menu = this.dropdown.find('.dropdown-menu');
+ const $menu = this.dropdown.find('.dropdown-menu');
$menu.addClass('dropdown-open-top');
$menu.css('top', 'initial');
@@ -544,10 +539,9 @@ GitLabDropdown.prototype.positionMenuAbove = function() {
};
GitLabDropdown.prototype.hidden = function(e) {
- var $input;
this.resetRows();
this.removeArrowKeyEvent();
- $input = this.dropdown.find('.dropdown-input-field');
+ const $input = this.dropdown.find('.dropdown-input-field');
if (this.options.filterable) {
$input.blur();
}
@@ -575,7 +569,7 @@ GitLabDropdown.prototype.appendMenu = function(html) {
};
GitLabDropdown.prototype.clearMenu = function() {
- var selector;
+ let selector;
selector = '.dropdown-content';
if (this.dropdown.find('.dropdown-toggle-page').length) {
if (this.options.containerSelector) {
@@ -635,10 +629,9 @@ GitLabDropdown.prototype.noResults = function() {
};
GitLabDropdown.prototype.rowClicked = function(el) {
- var field, groupName, isInput, selectedIndex, selectedObject, value, isMarking;
-
+ let field, groupName, selectedIndex, selectedObject, isMarking;
const { fieldName } = this.options;
- isInput = $(this.el).is('input');
+ const isInput = $(this.el).is('input');
if (this.renderedData) {
groupName = el.data('group');
if (groupName) {
@@ -662,7 +655,7 @@ GitLabDropdown.prototype.rowClicked = function(el) {
}
field = [];
- value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id;
+ const value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id;
if (isInput) {
field = $(this.el);
} else if (value != null) {
@@ -734,13 +727,12 @@ GitLabDropdown.prototype.focusTextInput = function() {
};
GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject, single) {
- var $input;
// Create hidden input for form
if (single) {
$(`input[name="${fieldName}"]`).remove();
}
- $input = $('<input>')
+ const $input = $('<input>')
.attr('type', 'hidden')
.attr('name', fieldName)
.val(value);
@@ -762,7 +754,7 @@ GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject, s
};
GitLabDropdown.prototype.selectRowAtIndex = function(index) {
- var $el, selector;
+ let selector;
// If we pass an option index
if (typeof index !== 'undefined') {
selector = `${SELECTABLE_CLASSES}:eq(${index}) a`;
@@ -773,9 +765,9 @@ GitLabDropdown.prototype.selectRowAtIndex = function(index) {
selector = `.dropdown-page-one ${selector}`;
}
// simulate a click on the first link
- $el = $(selector, this.dropdown);
+ const $el = $(selector, this.dropdown);
if ($el.length) {
- var href = $el.attr('href');
+ const href = $el.attr('href');
if (href && href !== '#') {
visitUrl(href);
} else {
@@ -785,15 +777,15 @@ GitLabDropdown.prototype.selectRowAtIndex = function(index) {
};
GitLabDropdown.prototype.addArrowKeyEvent = function() {
- var ARROW_KEY_CODES, selector;
- ARROW_KEY_CODES = [38, 40];
+ let selector;
+ const ARROW_KEY_CODES = [38, 40];
selector = SELECTABLE_CLASSES;
if (this.dropdown.find('.dropdown-toggle-page').length) {
selector = `.dropdown-page-one ${selector}`;
}
return $('body').on('keydown', e => {
- var $listItems, PREV_INDEX, currentKeyCode;
- currentKeyCode = e.which;
+ let $listItems, PREV_INDEX;
+ const currentKeyCode = e.which;
if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) {
e.preventDefault();
e.stopImmediatePropagation();
@@ -834,16 +826,6 @@ GitLabDropdown.prototype.resetRows = function resetRows() {
};
GitLabDropdown.prototype.highlightRowAtIndex = function($listItems, index) {
- var $dropdownContent,
- $listItem,
- dropdownContentBottom,
- dropdownContentHeight,
- dropdownContentTop,
- dropdownScrollTop,
- listItemBottom,
- listItemHeight,
- listItemTop;
-
if (!$listItems) {
$listItems = $(SELECTABLE_CLASSES, this.dropdown);
}
@@ -851,18 +833,18 @@ GitLabDropdown.prototype.highlightRowAtIndex = function($listItems, index) {
// Remove the class for the previously focused row
$('.is-focused', this.dropdown).removeClass('is-focused');
// Update the class for the row at the specific index
- $listItem = $listItems.eq(index);
+ const $listItem = $listItems.eq(index);
$listItem.find('a:first-child').addClass('is-focused');
// Dropdown content scroll area
- $dropdownContent = $listItem.closest('.dropdown-content');
- dropdownScrollTop = $dropdownContent.scrollTop();
- dropdownContentHeight = $dropdownContent.outerHeight();
- dropdownContentTop = $dropdownContent.prop('offsetTop');
- dropdownContentBottom = dropdownContentTop + dropdownContentHeight;
+ const $dropdownContent = $listItem.closest('.dropdown-content');
+ const dropdownScrollTop = $dropdownContent.scrollTop();
+ const dropdownContentHeight = $dropdownContent.outerHeight();
+ const dropdownContentTop = $dropdownContent.prop('offsetTop');
+ const dropdownContentBottom = dropdownContentTop + dropdownContentHeight;
// Get the offset bottom of the list item
- listItemHeight = $listItem.outerHeight();
- listItemTop = $listItem.prop('offsetTop');
- listItemBottom = listItemTop + listItemHeight;
+ const listItemHeight = $listItem.outerHeight();
+ const listItemTop = $listItem.prop('offsetTop');
+ const listItemBottom = listItemTop + listItemHeight;
if (!index) {
// Scroll the dropdown content to the top
$dropdownContent.scrollTop(0);
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue b/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue
index 7ce454b7338..7113b72f8c5 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue
@@ -169,12 +169,6 @@ export default {
<p v-if="shouldShowMetricsUnavailable" class="usage-info js-usage-info usage-info-unavailable">
{{ s__('mrWidget|Deployment statistics are not available currently') }}
</p>
- <memory-graph
- v-if="shouldShowMemoryGraph"
- :metrics="memoryMetrics"
- :deployment-time="deploymentTime"
- height="25"
- width="100"
- />
+ <memory-graph v-if="shouldShowMemoryGraph" :metrics="memoryMetrics" :height="25" :width="110" />
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.vue b/app/assets/javascripts/vue_shared/components/memory_graph.vue
index 26d7d8e8866..58fe3b2b28e 100644
--- a/app/assets/javascripts/vue_shared/components/memory_graph.vue
+++ b/app/assets/javascripts/vue_shared/components/memory_graph.vue
@@ -1,128 +1,43 @@
<script>
-import { __, sprintf } from '~/locale';
-import { getTimeago } from '../../lib/utils/datetime_utility';
+import { formatDate, secondsToMilliseconds } from '~/lib/utils/datetime_utility';
+import { GlSparklineChart } from '@gitlab/ui/dist/charts';
export default {
name: 'MemoryGraph',
+ components: {
+ GlSparklineChart,
+ },
props: {
metrics: { type: Array, required: true },
- deploymentTime: { type: Number, required: true },
- width: { type: String, required: true },
- height: { type: String, required: true },
- },
- data() {
- return {
- pathD: '',
- pathViewBox: '',
- dotX: '',
- dotY: '',
- };
+ width: { type: Number, required: true },
+ height: { type: Number, required: true },
},
computed: {
- getFormattedMedian() {
- const deployedSince = getTimeago().format(this.deploymentTime * 1000);
- return sprintf(__('Deployed %{deployedSince}'), { deployedSince });
+ chartData() {
+ return this.metrics.map(([x, y]) => [
+ this.getFormattedDeploymentTime(x),
+ this.getMemoryUsage(y),
+ ]);
},
},
- mounted() {
- this.renderGraph(this.deploymentTime, this.metrics);
- },
methods: {
- /**
- * Returns metric value index in metrics array
- * with timestamp closest to matching median
- */
- getMedianMetricIndex(median, metrics) {
- let matchIndex = 0;
- let timestampDiff = 0;
- let smallestDiff = 0;
-
- const metricTimestamps = metrics.map(v => v[0]);
-
- // Find metric timestamp which is closest to deploymentTime
- timestampDiff = Math.abs(metricTimestamps[0] - median);
- metricTimestamps.forEach((timestamp, index) => {
- if (index === 0) {
- // Skip first element
- return;
- }
-
- smallestDiff = Math.abs(timestamp - median);
- if (smallestDiff < timestampDiff) {
- matchIndex = index;
- timestampDiff = smallestDiff;
- }
- });
-
- return matchIndex;
+ getFormattedDeploymentTime(timestamp) {
+ return formatDate(new Date(secondsToMilliseconds(timestamp)), 'mmm dd yyyy HH:MM:s');
},
-
- /**
- * Get Graph Plotting values to render Line and Dot
- */
- getGraphPlotValues(median, metrics) {
- const renderData = metrics.map(v => v[1]);
- const medianMetricIndex = this.getMedianMetricIndex(median, metrics);
- let cx = 0;
- let cy = 0;
-
- // Find Maximum and Minimum values from `renderData` array
- const maxMemory = Math.max.apply(null, renderData);
- const minMemory = Math.min.apply(null, renderData);
-
- // Find difference between extreme ends
- const diff = maxMemory - minMemory;
- const lineWidth = renderData.length;
-
- // Iterate over metrics values and perform following
- // 1. Find x & y co-ords for deploymentTime's memory value
- // 2. Return line path against maxMemory
- const linePath = renderData.map((y, x) => {
- if (medianMetricIndex === x) {
- cx = x;
- cy = maxMemory - y;
- }
- return `${x} ${maxMemory - y}`;
- });
-
- return {
- pathD: linePath,
- pathViewBox: {
- lineWidth,
- diff,
- },
- dotX: cx,
- dotY: cy,
- };
- },
-
- /**
- * Render Graph based on provided median and metrics values
- */
- renderGraph(median, metrics) {
- const { pathD, pathViewBox, dotX, dotY } = this.getGraphPlotValues(median, metrics);
-
- // Set props and update graph on UI.
- this.pathD = `M ${pathD}`;
- this.pathViewBox = `0 0 ${pathViewBox.lineWidth} ${pathViewBox.diff}`;
- this.dotX = dotX;
- this.dotY = dotY;
+ getMemoryUsage(MBs) {
+ return Number(MBs).toFixed(2);
},
},
};
</script>
<template>
- <div class="memory-graph-container">
- <svg
- :title="getFormattedMedian"
- :width="width"
+ <div class="memory-graph-container p-1" :style="{ width: `${width}px` }">
+ <gl-sparkline-chart
:height="height"
- class="has-tooltip"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path :d="pathD" :viewBox="pathViewBox" />
- <circle :cx="dotX" :cy="dotY" r="1.5" transform="translate(0 -1)" />
- </svg>
+ :tooltip-label="__('MB')"
+ :show-last-y-value="false"
+ :data="chartData"
+ />
</div>
</template>
diff --git a/app/assets/stylesheets/framework/memory_graph.scss b/app/assets/stylesheets/framework/memory_graph.scss
index c84010c6f10..06e1ebe41be 100644
--- a/app/assets/stylesheets/framework/memory_graph.scss
+++ b/app/assets/stylesheets/framework/memory_graph.scss
@@ -1,18 +1,4 @@
.memory-graph-container {
- svg {
- background: $white-light;
- border: 1px solid $gray-200;
- }
-
- path {
- fill: none;
- stroke: $blue-500;
- stroke-width: 2px;
- }
-
- circle {
- stroke: $blue-700;
- fill: $blue-700;
- stroke-width: 4px;
- }
+ background: $white-light;
+ border: 1px solid $gray-200;
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 971f3b2c308..eb7d162e38c 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -949,7 +949,6 @@
.deployment-info {
flex: 1;
white-space: nowrap;
- overflow: hidden;
text-overflow: ellipsis;
min-width: 100px;
diff --git a/app/controllers/projects/error_tracking_controller.rb b/app/controllers/projects/error_tracking_controller.rb
index 3969f33abe9..78d3854e72d 100644
--- a/app/controllers/projects/error_tracking_controller.rb
+++ b/app/controllers/projects/error_tracking_controller.rb
@@ -111,7 +111,7 @@ class Projects::ErrorTrackingController < Projects::ApplicationController
end
def list_issues_params
- params.permit(:search_term)
+ params.permit([:search_term, :sort])
end
def list_projects_params
diff --git a/app/graphql/mutations/todos/restore.rb b/app/graphql/mutations/todos/restore.rb
new file mode 100644
index 00000000000..c4597bd84a2
--- /dev/null
+++ b/app/graphql/mutations/todos/restore.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Todos
+ class Restore < ::Mutations::Todos::Base
+ graphql_name 'TodoRestore'
+
+ authorize :update_todo
+
+ argument :id,
+ GraphQL::ID_TYPE,
+ required: true,
+ description: 'The global id of the todo to restore'
+
+ field :todo, Types::TodoType,
+ null: false,
+ description: 'The requested todo'
+
+ def resolve(id:)
+ todo = authorized_find!(id: id)
+ restore(todo.id) if todo.done?
+
+ {
+ todo: todo.reset,
+ errors: errors_on_object(todo)
+ }
+ end
+
+ private
+
+ def restore(id)
+ TodoService.new.mark_todos_as_pending_by_ids([id], current_user)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index b3c7c162bb3..a45f3629621 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -21,6 +21,7 @@ module Types
mount_mutation Mutations::Notes::Update
mount_mutation Mutations::Notes::Destroy
mount_mutation Mutations::Todos::MarkDone
+ mount_mutation Mutations::Todos::Restore
end
end
diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb
index 1093efee85a..6ad086211fb 100644
--- a/app/models/clusters/applications/knative.rb
+++ b/app/models/clusters/applications/knative.rb
@@ -7,7 +7,7 @@ module Clusters
REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'
METRICS_CONFIG = 'https://storage.googleapis.com/triggermesh-charts/istio-metrics.yaml'
FETCH_IP_ADDRESS_DELAY = 30.seconds
- API_RESOURCES_PATH = 'config/knative/api_resources.yml'
+ API_GROUPS_PATH = 'config/knative/api_groups.yml'
self.table_name = 'clusters_applications_knative'
@@ -109,15 +109,15 @@ module Clusters
end
def delete_knative_and_istio_crds
- api_resources.map do |crd|
- Gitlab::Kubernetes::KubectlCmd.delete("--ignore-not-found", "crd", "#{crd}")
+ api_groups.map do |group|
+ Gitlab::Kubernetes::KubectlCmd.delete_crds_from_group(group)
end
end
# returns an array of CRDs to be postdelete since helm does not
# manage the CRDs it creates.
- def api_resources
- @api_resources ||= YAML.safe_load(File.read(Rails.root.join(API_RESOURCES_PATH)))
+ def api_groups
+ @api_groups ||= YAML.safe_load(File.read(Rails.root.join(API_GROUPS_PATH)))
end
def install_knative_metrics
diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb
index 2aa058a243f..5b5c1b1b56b 100644
--- a/app/models/error_tracking/project_error_tracking_setting.rb
+++ b/app/models/error_tracking/project_error_tracking_setting.rb
@@ -5,6 +5,7 @@ module ErrorTracking
include Gitlab::Utils::StrongMemoize
include ReactiveCaching
+ SENTRY_API_ERROR_TYPE_BAD_REQUEST = 'bad_request_for_sentry_api'
SENTRY_API_ERROR_TYPE_MISSING_KEYS = 'missing_keys_in_sentry_response'
SENTRY_API_ERROR_TYPE_NON_20X_RESPONSE = 'non_20x_response_from_sentry'
SENTRY_API_ERROR_INVALID_SIZE = 'invalid_size_of_sentry_response'
@@ -119,6 +120,8 @@ module ErrorTracking
{ error: e.message, error_type: SENTRY_API_ERROR_TYPE_MISSING_KEYS }
rescue Sentry::Client::ResponseInvalidSizeError => e
{ error: e.message, error_type: SENTRY_API_ERROR_INVALID_SIZE }
+ rescue Sentry::Client::BadRequestError => e
+ { error: e.message, error_type: SENTRY_API_ERROR_TYPE_BAD_REQUEST }
end
# http://HOST/api/0/projects/ORG/PROJECT
diff --git a/app/services/error_tracking/list_issues_service.rb b/app/services/error_tracking/list_issues_service.rb
index 0c2183dc36e..a2c8e6e6619 100644
--- a/app/services/error_tracking/list_issues_service.rb
+++ b/app/services/error_tracking/list_issues_service.rb
@@ -4,6 +4,7 @@ module ErrorTracking
class ListIssuesService < ErrorTracking::BaseService
DEFAULT_ISSUE_STATUS = 'unresolved'
DEFAULT_LIMIT = 20
+ DEFAULT_SORT = 'last_seen'
def execute
return error('Error Tracking is not enabled') unless enabled?
@@ -12,7 +13,8 @@ module ErrorTracking
result = project_error_tracking_setting.list_sentry_issues(
issue_status: issue_status,
limit: limit,
- search_term: search_term
+ search_term: search_term,
+ sort: sort
)
# our results are not yet ready
@@ -33,10 +35,6 @@ module ErrorTracking
private
- def fetch
- project_error_tracking_setting.list_sentry_issues(issue_status: issue_status, limit: limit)
- end
-
def parse_response(response)
{ issues: response[:issues] }
end
@@ -60,5 +58,9 @@ module ErrorTracking
def can_read?
can?(current_user, :read_sentry_issue, project)
end
+
+ def sort
+ params[:sort] || DEFAULT_SORT
+ end
end
end
diff --git a/changelogs/unreleased/31391-update-sparkline-chart-deployment-widget.yml b/changelogs/unreleased/31391-update-sparkline-chart-deployment-widget.yml
new file mode 100644
index 00000000000..c16e1770f30
--- /dev/null
+++ b/changelogs/unreleased/31391-update-sparkline-chart-deployment-widget.yml
@@ -0,0 +1,5 @@
+---
+title: Improve sparkline chart in MR widget deployment
+merge_request: 20085
+author:
+type: other
diff --git a/changelogs/unreleased/31914-graphql-todo-restore-pd.yml b/changelogs/unreleased/31914-graphql-todo-restore-pd.yml
new file mode 100644
index 00000000000..37af6be8276
--- /dev/null
+++ b/changelogs/unreleased/31914-graphql-todo-restore-pd.yml
@@ -0,0 +1,5 @@
+---
+title: Add GraphQL mutation to restore a Todo
+merge_request: 20261
+author:
+type: added
diff --git a/changelogs/unreleased/34068-sort-by-first-seen-last-seen-and-frequency-in-sentry-error-list-in-.yml b/changelogs/unreleased/34068-sort-by-first-seen-last-seen-and-frequency-in-sentry-error-list-in-.yml
new file mode 100644
index 00000000000..5bcf520f6cb
--- /dev/null
+++ b/changelogs/unreleased/34068-sort-by-first-seen-last-seen-and-frequency-in-sentry-error-list-in-.yml
@@ -0,0 +1,5 @@
+---
+title: Add sort param to error tracking issue index
+merge_request: 20101
+author:
+type: changed
diff --git a/changelogs/unreleased/34636-drop-deprecated-column-from-projects-table.yml b/changelogs/unreleased/34636-drop-deprecated-column-from-projects-table.yml
new file mode 100644
index 00000000000..f6dae1e8ce9
--- /dev/null
+++ b/changelogs/unreleased/34636-drop-deprecated-column-from-projects-table.yml
@@ -0,0 +1,5 @@
+---
+title: Drop deprecated column from projects table
+merge_request: 18914
+author:
+type: deprecated
diff --git a/changelogs/unreleased/Remove-var-from-gl_dropdown-js.yml b/changelogs/unreleased/Remove-var-from-gl_dropdown-js.yml
new file mode 100644
index 00000000000..7edfaff56af
--- /dev/null
+++ b/changelogs/unreleased/Remove-var-from-gl_dropdown-js.yml
@@ -0,0 +1,5 @@
+---
+title: replace var gl_dropdown.js
+merge_request: 20166
+author: nuwe1
+type: other
diff --git a/config/initializers/hamlit.rb b/config/initializers/hamlit.rb
index 51dbffeda05..b5bcae4bbfc 100644
--- a/config/initializers/hamlit.rb
+++ b/config/initializers/hamlit.rb
@@ -1,18 +1,4 @@
-module Hamlit
- class TemplateHandler
- def call(template)
- Engine.new(
- generator: Temple::Generators::RailsOutputBuffer,
- attr_quote: '"'
- ).call(template.source)
- end
- end
-end
-
-ActionView::Template.register_template_handler(
- :haml,
- Hamlit::TemplateHandler.new
-)
+Hamlit::RailsTemplate.set_options(attr_quote: '"')
Hamlit::Filters.remove_filter('coffee')
Hamlit::Filters.remove_filter('coffeescript')
diff --git a/config/knative/api_groups.yml b/config/knative/api_groups.yml
new file mode 100644
index 00000000000..4697086eb5a
--- /dev/null
+++ b/config/knative/api_groups.yml
@@ -0,0 +1,11 @@
+---
+
+- networking.istio.io
+- rbac.istio.io
+- authentication.istio.io
+- config.istio.io
+
+- networking.internal.knative.dev
+- serving.knative.dev
+- caching.internal.knative.dev
+- autoscaling.internal.knative.dev \ No newline at end of file
diff --git a/config/knative/api_resources.yml b/config/knative/api_resources.yml
deleted file mode 100644
index 095f44ed799..00000000000
--- a/config/knative/api_resources.yml
+++ /dev/null
@@ -1,70 +0,0 @@
----
-
-- meshpolicies.authentication.istio.io
-- policies.authentication.istio.io
-- adapters.config.istio.io
-- apikeys.config.istio.io
-- attributemanifests.config.istio.io
-- authorizations.config.istio.io
-- bypasses.config.istio.io
-- podautoscalers.autoscaling.internal.knative.dev
-- builds.build.knative.dev
-- buildtemplates.build.knative.dev
-- clusterbuildtemplates.build.knative.dev
-- images.caching.internal.knative.dev
-- certificates.networking.internal.knative.dev
-- clusteringresses.networking.internal.knative.dev
-- serverlessservices.networking.internal.knative.dev
-- configurations.serving.knative.dev
-- revisions.serving.knative.dev
-- routes.serving.knative.dev
-- services.serving.knative.dev
-- checknothings.config.istio.io
-- circonuses.config.istio.io
-- deniers.config.istio.io
-- edges.config.istio.io
-- fluentds.config.istio.io
-- handlers.config.istio.io
-- httpapispecbindings.config.istio.io
-- httpapispecs.config.istio.io
-- instances.config.istio.io
-- kubernetesenvs.config.istio.io
-- kuberneteses.config.istio.io
-- listcheckers.config.istio.io
-- listentries.config.istio.io
-- logentries.config.istio.io
-- memquotas.config.istio.io
-- metrics.config.istio.io
-- noops.config.istio.io
-- opas.config.istio.io
-- prometheuses.config.istio.io
-- quotas.config.istio.io
-- quotaspecbindings.config.istio.io
-- quotaspecs.config.istio.io
-- rbacs.config.istio.io
-- redisquotas.config.istio.io
-- reportnothings.config.istio.io
-- rules.config.istio.io
-- servicecontrolreports.config.istio.io
-- servicecontrols.config.istio.io
-- signalfxs.config.istio.io
-- solarwindses.config.istio.io
-- stackdrivers.config.istio.io
-- statsds.config.istio.io
-- stdios.config.istio.io
-- templates.config.istio.io
-- tracespans.config.istio.io
-- destinationrules.networking.istio.io
-- envoyfilters.networking.istio.io
-- gateways.networking.istio.io
-- serviceentries.networking.istio.io
-- virtualservices.networking.istio.io
-- rbacconfigs.rbac.istio.io
-- servicerolebindings.rbac.istio.io
-- serviceroles.rbac.istio.io
-- cloudwatches.config.istio.io
-- clusterrbacconfigs.rbac.istio.io
-- dogstatsds.config.istio.io
-- ingresses.networking.internal.knative.dev
-- sidecars.networking.istio.io
-- zipkins.config.istio.io
diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb
index 1e260236dc5..46b114dd07b 100644
--- a/db/fixtures/development/01_admin.rb
+++ b/db/fixtures/development/01_admin.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Gitlab::Seeder.quiet do
User.create!(
diff --git a/db/fixtures/development/03_project.rb b/db/fixtures/development/03_project.rb
index 19800a65cab..33df3ed7156 100644
--- a/db/fixtures/development/03_project.rb
+++ b/db/fixtures/development/03_project.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
class Gitlab::Seeder::Projects
include ActionView::Helpers::NumberHelper
diff --git a/db/fixtures/development/06_teams.rb b/db/fixtures/development/06_teams.rb
index 79ea96bf30e..94d3aa59710 100644
--- a/db/fixtures/development/06_teams.rb
+++ b/db/fixtures/development/06_teams.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do
diff --git a/db/fixtures/development/07_milestones.rb b/db/fixtures/development/07_milestones.rb
index 1194bb3fe6f..8a282562335 100644
--- a/db/fixtures/development/07_milestones.rb
+++ b/db/fixtures/development/07_milestones.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Gitlab::Seeder.quiet do
Project.not_mass_generated.each do |project|
diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb
index 582a5203d1d..7487a57fdb6 100644
--- a/db/fixtures/development/09_issues.rb
+++ b/db/fixtures/development/09_issues.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Gitlab::Seeder.quiet do
Rake::Task["gitlab:seed:issues"].invoke
diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb
index 29f2fabbd5f..77650ebb1bc 100644
--- a/db/fixtures/development/10_merge_requests.rb
+++ b/db/fixtures/development/10_merge_requests.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Gitlab::Seeder.quiet do
# Limit the number of merge requests per project to avoid long seeds
diff --git a/db/fixtures/development/11_keys.rb b/db/fixtures/development/11_keys.rb
index 13eadc35e07..eeee2388d01 100644
--- a/db/fixtures/development/11_keys.rb
+++ b/db/fixtures/development/11_keys.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
# Creating keys runs a gitlab-shell worker. Since we may not have the right
diff --git a/db/fixtures/development/12_snippets.rb b/db/fixtures/development/12_snippets.rb
index 0ee9058a20b..3fa172c8f0f 100644
--- a/db/fixtures/development/12_snippets.rb
+++ b/db/fixtures/development/12_snippets.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Gitlab::Seeder.quiet do
content =<<eos
diff --git a/db/fixtures/development/13_comments.rb b/db/fixtures/development/13_comments.rb
index bc2d74c8034..8f63ca302c6 100644
--- a/db/fixtures/development/13_comments.rb
+++ b/db/fixtures/development/13_comments.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Gitlab::Seeder.quiet do
Issue.find_each do |issue|
diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb
index 468caac23f9..4e9131c1a46 100644
--- a/db/fixtures/development/14_pipelines.rb
+++ b/db/fixtures/development/14_pipelines.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
class Gitlab::Seeder::Pipelines
STAGES = %w[build test deploy notify]
diff --git a/db/fixtures/development/15_award_emoji.rb b/db/fixtures/development/15_award_emoji.rb
index a9dcc048586..2b69a6c26ab 100644
--- a/db/fixtures/development/15_award_emoji.rb
+++ b/db/fixtures/development/15_award_emoji.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Gitlab::Seeder.quiet do
EMOJI = Gitlab::Emoji.emojis.keys
diff --git a/db/fixtures/development/16_protected_branches.rb b/db/fixtures/development/16_protected_branches.rb
index 2b492ac1f61..e6615b20f9c 100644
--- a/db/fixtures/development/16_protected_branches.rb
+++ b/db/fixtures/development/16_protected_branches.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Gitlab::Seeder.quiet do
admin_user = User.find(1)
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb
index 606a4cb1dde..2532b71ad26 100644
--- a/db/fixtures/development/17_cycle_analytics.rb
+++ b/db/fixtures/development/17_cycle_analytics.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
require './spec/support/helpers/test_env'
class Gitlab::Seeder::CycleAnalytics
diff --git a/db/fixtures/development/19_environments.rb b/db/fixtures/development/19_environments.rb
index 08363804216..124f9d74ddd 100644
--- a/db/fixtures/development/19_environments.rb
+++ b/db/fixtures/development/19_environments.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
class Gitlab::Seeder::Environments
def initialize(project)
diff --git a/db/fixtures/development/20_nested_groups.rb b/db/fixtures/development/20_nested_groups.rb
index 3d95e243f8a..8f64dfbd644 100644
--- a/db/fixtures/development/20_nested_groups.rb
+++ b/db/fixtures/development/20_nested_groups.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do
diff --git a/db/fixtures/development/24_forks.rb b/db/fixtures/development/24_forks.rb
index fa16b2a1d93..cb6dbb7504d 100644
--- a/db/fixtures/development/24_forks.rb
+++ b/db/fixtures/development/24_forks.rb
@@ -1,4 +1,4 @@
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do
diff --git a/db/fixtures/development/25_api_personal_access_token.rb b/db/fixtures/development/25_api_personal_access_token.rb
index a2e6c674c1f..0a8fd86b440 100644
--- a/db/fixtures/development/25_api_personal_access_token.rb
+++ b/db/fixtures/development/25_api_personal_access_token.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require './spec/support/sidekiq'
+require './spec/support/sidekiq_middleware'
# Create an api access token for root user with the value: ypCa3Dzb23o5nvsixwPA
Gitlab::Seeder.quiet do
diff --git a/db/post_migrate/20191015154408_drop_merge_requests_require_code_owner_approval_from_projects.rb b/db/post_migrate/20191015154408_drop_merge_requests_require_code_owner_approval_from_projects.rb
new file mode 100644
index 00000000000..a1d763b7ed1
--- /dev/null
+++ b/db/post_migrate/20191015154408_drop_merge_requests_require_code_owner_approval_from_projects.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class DropMergeRequestsRequireCodeOwnerApprovalFromProjects < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ remove_column :projects, :merge_requests_require_code_owner_approval, :boolean
+ end
+
+ def down
+ add_column :projects, :merge_requests_require_code_owner_approval, :boolean
+
+ add_concurrent_index(
+ :projects,
+ %i[archived pending_delete merge_requests_require_code_owner_approval],
+ name: 'projects_requiring_code_owner_approval',
+ where: '((pending_delete = false) AND (archived = false) AND (merge_requests_require_code_owner_approval = true))'
+ )
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 21d045be380..652f4a316e1 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -3186,7 +3186,6 @@ ActiveRecord::Schema.define(version: 2019_11_18_182722) do
t.bigint "pool_repository_id"
t.string "runners_token_encrypted"
t.string "bfg_object_map"
- t.boolean "merge_requests_require_code_owner_approval"
t.boolean "detected_repository_languages"
t.boolean "merge_requests_disable_committers_approval"
t.boolean "require_password_to_approve"
@@ -3198,7 +3197,6 @@ ActiveRecord::Schema.define(version: 2019_11_18_182722) do
t.date "marked_for_deletion_at"
t.integer "marked_for_deletion_by_user_id"
t.index "lower((name)::text)", name: "index_projects_on_lower_name"
- t.index ["archived", "pending_delete", "merge_requests_require_code_owner_approval"], name: "projects_requiring_code_owner_approval", where: "((pending_delete = false) AND (archived = false) AND (merge_requests_require_code_owner_approval = true))"
t.index ["created_at", "id"], name: "index_projects_on_created_at_and_id"
t.index ["creator_id"], name: "index_projects_on_creator_id"
t.index ["description"], name: "index_projects_on_description_trigram", opclass: :gin_trgm_ops, using: :gin
diff --git a/doc/administration/auth/smartcard.md b/doc/administration/auth/smartcard.md
index 85ed4355e80..1088d1446fd 100644
--- a/doc/administration/auth/smartcard.md
+++ b/doc/administration/auth/smartcard.md
@@ -4,6 +4,10 @@ type: reference
# Smartcard authentication **(PREMIUM ONLY)**
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/33669) in GitLab 12.6,
+if a user has a pre-existing username and password, they can still use that to log
+in by default. However, this can be disabled.
+
GitLab supports authentication using smartcards.
## Authentication methods
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 12ae1fc3029..fc025d98e3d 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -3519,6 +3519,7 @@ type Mutation {
mergeRequestSetWip(input: MergeRequestSetWipInput!): MergeRequestSetWipPayload
removeAwardEmoji(input: RemoveAwardEmojiInput!): RemoveAwardEmojiPayload
todoMarkDone(input: TodoMarkDoneInput!): TodoMarkDonePayload
+ todoRestore(input: TodoRestoreInput!): TodoRestorePayload
toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload
updateEpic(input: UpdateEpicInput!): UpdateEpicPayload
updateNote(input: UpdateNoteInput!): UpdateNotePayload
@@ -4992,6 +4993,41 @@ type TodoMarkDonePayload {
todo: Todo!
}
+"""
+Autogenerated input type of TodoRestore
+"""
+input TodoRestoreInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The global id of the todo to restore
+ """
+ id: ID!
+}
+
+"""
+Autogenerated return type of TodoRestore
+"""
+type TodoRestorePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Reasons why the mutation failed.
+ """
+ errors: [String!]!
+
+ """
+ The requested todo
+ """
+ todo: Todo!
+}
+
enum TodoStateEnum {
done
pending
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 7bbbe823697..a11d71bcd08 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -14329,6 +14329,33 @@
"deprecationReason": null
},
{
+ "name": "todoRestore",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "TodoRestoreInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "TodoRestorePayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "toggleAwardEmoji",
"description": null,
"args": [
@@ -16694,6 +16721,112 @@
},
{
"kind": "OBJECT",
+ "name": "TodoRestorePayload",
+ "description": "Autogenerated return type of TodoRestore",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Reasons why the mutation failed.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "todo",
+ "description": "The requested todo",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "Todo",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
+ "name": "TodoRestoreInput",
+ "description": "Autogenerated input type of TodoRestore",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "id",
+ "description": "The global id of the todo to restore",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "DesignManagementUploadPayload",
"description": "Autogenerated return type of DesignManagementUpload",
"fields": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 151e43f4cff..7f99028848b 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -777,6 +777,14 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
| `errors` | String! => Array | Reasons why the mutation failed. |
| `todo` | Todo! | The requested todo |
+### TodoRestorePayload
+
+| Name | Type | Description |
+| --- | ---- | ---------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Reasons why the mutation failed. |
+| `todo` | Todo! | The requested todo |
+
### ToggleAwardEmojiPayload
| Name | Type | Description |
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 7074d0249ef..541aa03450f 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -1407,7 +1407,7 @@ If the merge request is already merged or closed - you get `405` and error messa
In case the merge request is not set to be merged when the pipeline succeeds, you'll also get a `406` error.
```
-PUT /projects/:id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds
+POST /projects/:id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds
```
Parameters:
diff --git a/doc/development/packages.md b/doc/development/packages.md
index 6d4a9ea9f41..30f22c13525 100644
--- a/doc/development/packages.md
+++ b/doc/development/packages.md
@@ -5,7 +5,7 @@ This document will guide you through adding another [package management system](
See already supported package types in [Packages documentation](../administration/packages/index.md)
Since GitLab packages' UI is pretty generic, it is possible to add basic new
-package system support by solely backend changes. This guide is superficial and does
+package system support with solely backend changes. This guide is superficial and does
not cover the way the code should be written. However, you can find a good example
by looking at existing merge requests with Maven and NPM support:
@@ -14,6 +14,35 @@ by looking at existing merge requests with Maven and NPM support:
- [Maven repository](https://gitlab.com/gitlab-org/gitlab/merge_requests/6607).
- [Instance level endpoint for Maven repository](https://gitlab.com/gitlab-org/gitlab/merge_requests/8757)
+## Suggested contributions
+
+The goal of the Package group is to build a set of features that, within three years, will allow ninety percent of our customers to store all of their packages in GitLab. To do that we need to ensure that we support the below package manager formats.
+
+| Format | Use case |
+| ------ | ------ |
+| [Bower](https://gitlab.com/gitlab-org/gitlab/issues/36888) | Boost your front end development by hosting your own Bower components. |
+| [Chef](https://gitlab.com/gitlab-org/gitlab/issues/36889) | Configuration management with Chef using all the benefits of a repository manager. |
+| [CocoaPods](https://gitlab.com/gitlab-org/gitlab/issues/36890) | Speed up development with Xcode and CocoaPods. |
+| [Conan](https://docs.gitlab.com/ee/user/packages/conan_repository/) *12.6+* | A standardized way to share and version control C/C++ libraries across projects. |
+| [Conda](https://gitlab.com/gitlab-org/gitlab/issues/36891) | Secure and private local Conda repositories. |
+| [CRAN](https://gitlab.com/gitlab-org/gitlab/issues/36892) | Deploy and resolve CRAN packages for the R language. |
+| [Debian](https://gitlab.com/gitlab-org/gitlab/issues/5835) | Host and provision Debian packages. |
+| [Docker](https://docs.gitlab.com/ee/user/packages/container_registry/) *8.8+* | Host your own secure private Docker registries and proxy external Docker registries such as Docker Hub. |
+| [Go](https://gitlab.com/gitlab-org/gitlab/issues/9773) | Resolve Go dependencies from and publish your Go packages to GitLab. |
+| [Helm](https://gitlab.com/gitlab-org/gitlab/issues/18997) | Manage your Helm Charts in GitLab and gain control over deployments to your Kubernetes cluster. |
+| [Maven](https://docs.gitlab.com/ee/user/packages/maven_repository/index.html) *11.3+*| The GitLab Maven Repository enables every project in GitLab to have its own space to store Maven packages. |
+| [npm](https://docs.gitlab.com/ee/user/packages/npm_registry/index.html) *11.7+* | Host your own node.js packages. |
+| [NuGet](https://gitlab.com/gitlab-org/gitlab/issues/20050) *Planned for 12.7*| Host NuGet packages in GitLab, and pull libraries into your various Visual Studio .NET applications. |
+| [Opkg](https://gitlab.com/gitlab-org/gitlab/issues/36894) | Optimize your work with OpenWrt using Opkg repositories. |
+| [P2](https://gitlab.com/gitlab-org/gitlab/issues/36895) | Host all your Eclipse plugins in your own GitLab P2 repository. |
+| [PHP Composer](https://gitlab.com/gitlab-org/gitlab/issues/15886) | Provision Composer packages from GitLab and access Packagist and other remote Composer metadata repositories. |
+| [Puppet](https://gitlab.com/gitlab-org/gitlab/issues/36897) | Configuration management meets repository management with Puppet repositories. |
+| [PyPi](https://gitlab.com/gitlab-org/gitlab/issues/10483) | Host PyPi distributions. |
+| [RPM](https://gitlab.com/gitlab-org/gitlab/issues/5932) | Distribute RPMs directly from GitLab. |
+| [RubyGems](https://gitlab.com/gitlab-org/gitlab/issues/803) | Use GitLab to host your own gems. |
+| [SBT](https://gitlab.com/gitlab-org/gitlab/issues/36898) | Resolve dependencies from and deploy build output to SBT repositories when running SBT builds. |
+| [Vagrant](https://gitlab.com/gitlab-org/gitlab/issues/36899) | Securely host your Vagrant boxes in local repositories. |
+
## General information
The existing database model requires the following:
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 0e46052b0bd..4ceefc24e68 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -127,23 +127,23 @@ dependency_scanning:
Dependency Scanning can be [configured](#customizing-the-dependency-scanning-settings)
using environment variables.
-| Environment variable | Description | Example usage |
-| --------------------------------------- | ----------- | ------------- |
-| `DS_ANALYZER_IMAGES` | Comma separated list of custom images. The official default images are still enabled. Read more about [customizing analyzers](analyzers.md). | |
-| `DS_ANALYZER_IMAGE_PREFIX` | Override the name of the Docker registry providing the official default images (proxy). Read more about [customizing analyzers](analyzers.md). | |
-| `DS_ANALYZER_IMAGE_TAG` | Override the Docker tag of the official default images. Read more about [customizing analyzers](analyzers.md). | |
-| `DS_PYTHON_VERSION` | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12296) in GitLab 12.1)| |
-| `DS_PIP_DEPENDENCY_PATH` | Path to load Python pip dependencies from. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12412) in GitLab 12.2) | |
-| `DS_DEFAULT_ANALYZERS` | Override the names of the official default images. Read more about [customizing analyzers](analyzers.md). | |
-| `DS_DISABLE_DIND` | Disable Docker in Docker and run analyzers [individually](#disabling-docker-in-docker-for-dependency-scanning).| |
-| `DS_PULL_ANALYZER_IMAGES` | Pull the images from the Docker registry (set to `0` to disable). | |
-| `DS_EXCLUDED_PATHS` | Exclude vulnerabilities from output based on the paths. A comma-separated list of patterns. Patterns can be globs, file or folder paths. Parent directories will also match patterns. | `DS_EXCLUDED_PATHS=doc,spec` |
-| `DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT` | Time limit for Docker client negotiation. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. | |
-| `DS_PULL_ANALYZER_IMAGE_TIMEOUT` | Time limit when pulling the image of an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. | |
-| `DS_RUN_ANALYZER_TIMEOUT` | Time limit when running an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. | |
-| `PIP_INDEX_URL` | Base URL of Python Package Index (default `https://pypi.org/simple`). | |
-| `PIP_EXTRA_INDEX_URL` | Array of [extra URLs](https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-extra-index-url) of package indexes to use in addition to `PIP_INDEX_URL`. Comma separated. | |
-| `MAVEN_CLI_OPTS` | List of command line arguments that will be passed to the maven analyzer during the project's build phase (see example for [using private repos](#using-private-maven-repos)). | |
+| Environment variable | Description |
+| --------------------------------------- | ----------- |
+| `DS_ANALYZER_IMAGES` | Comma separated list of custom images. The official default images are still enabled. Read more about [customizing analyzers](analyzers.md). |
+| `DS_ANALYZER_IMAGE_PREFIX` | Override the name of the Docker registry providing the official default images (proxy). Read more about [customizing analyzers](analyzers.md). |
+| `DS_ANALYZER_IMAGE_TAG` | Override the Docker tag of the official default images. Read more about [customizing analyzers](analyzers.md). |
+| `DS_PYTHON_VERSION` | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12296) in GitLab 12.1)|
+| `DS_PIP_DEPENDENCY_PATH` | Path to load Python pip dependencies from. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12412) in GitLab 12.2) |
+| `DS_DEFAULT_ANALYZERS` | Override the names of the official default images. Read more about [customizing analyzers](analyzers.md). |
+| `DS_DISABLE_DIND` | Disable Docker in Docker and run analyzers [individually](#disabling-docker-in-docker-for-dependency-scanning).|
+| `DS_PULL_ANALYZER_IMAGES` | Pull the images from the Docker registry (set to `0` to disable). |
+| `DS_EXCLUDED_PATHS` | Exclude vulnerabilities from output based on the paths. A comma-separated list of patterns. Patterns can be globs, file or folder paths (e.g., `doc,spec`). Parent directories will also match patterns. |
+| `DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT` | Time limit for Docker client negotiation. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. |
+| `DS_PULL_ANALYZER_IMAGE_TIMEOUT` | Time limit when pulling the image of an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. |
+| `DS_RUN_ANALYZER_TIMEOUT` | Time limit when running an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. |
+| `PIP_INDEX_URL` | Base URL of Python Package Index (default `https://pypi.org/simple`). |
+| `PIP_EXTRA_INDEX_URL` | Array of [extra URLs](https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-extra-index-url) of package indexes to use in addition to `PIP_INDEX_URL`. Comma separated. |
+| `MAVEN_CLI_OPTS` | List of command line arguments that will be passed to the maven analyzer during the project's build phase (see example for [using private repos](#using-private-maven-repos)). |
### Using private Maven repos
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index f427f33c8c2..8b8d578a78e 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -206,14 +206,14 @@ The following are Docker image-related variables.
Some analyzers make it possible to filter out vulnerabilities under a given threshold.
-| Environment variable | Default value | Description | Example usage |
-|----------------------|---------------|-------------|---|
-| `SAST_BANDIT_EXCLUDED_PATHS` | - | comma-separated list of paths to exclude from scan. Uses Python's [`fnmatch` syntax](https://docs.python.org/2/library/fnmatch.html) | |
-| `SAST_BRAKEMAN_LEVEL` | 1 | Ignore Brakeman vulnerabilities under given confidence level. Integer, 1=Low 3=High. | |
-| `SAST_FLAWFINDER_LEVEL` | 1 | Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk. | |
-| `SAST_GITLEAKS_ENTROPY_LEVEL` | 8.0 | Minimum entropy for secret detection. Float, 0.0 = low, 8.0 = high. | |
-| `SAST_GOSEC_LEVEL` | 0 | Ignore gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 2=Medium, 3=High. | |
-| `SAST_EXCLUDED_PATHS` | - | Exclude vulnerabilities from output based on the paths. This is a comma-separated list of patterns. Patterns can be globs, file or folder paths. Parent directories will also match patterns. | `SAST_EXCLUDED_PATHS=doc,spec` |
+| Environment variable | Default value | Description |
+|----------------------|---------------|-------------|
+| `SAST_BANDIT_EXCLUDED_PATHS` | - | comma-separated list of paths to exclude from scan. Uses Python's [`fnmatch` syntax](https://docs.python.org/2/library/fnmatch.html) |
+| `SAST_BRAKEMAN_LEVEL` | 1 | Ignore Brakeman vulnerabilities under given confidence level. Integer, 1=Low 3=High. |
+| `SAST_FLAWFINDER_LEVEL` | 1 | Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk. |
+| `SAST_GITLEAKS_ENTROPY_LEVEL` | 8.0 | Minimum entropy for secret detection. Float, 0.0 = low, 8.0 = high. |
+| `SAST_GOSEC_LEVEL` | 0 | Ignore gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 2=Medium, 3=High. |
+| `SAST_EXCLUDED_PATHS` | - | Exclude vulnerabilities from output based on the paths. This is a comma-separated list of patterns. Patterns can be globs, file or folder paths (e.g., `doc,spec` ). Parent directories will also match patterns. |
#### Timeouts
diff --git a/doc/user/packages/index.md b/doc/user/packages/index.md
index 9873bd80e8b..ecaad960340 100644
--- a/doc/user/packages/index.md
+++ b/doc/user/packages/index.md
@@ -13,8 +13,12 @@ The Packages feature allows GitLab to act as a repository for the following:
| [Conan Repository](conan_repository/index.md) **(PREMIUM)** | The GitLab Conan Repository enables every project in GitLab to have its own space to store [Conan](https://conan.io/) packages. | 12.4+ |
| [Maven Repository](maven_repository/index.md) **(PREMIUM)** | The GitLab Maven Repository enables every project in GitLab to have its own space to store [Maven](https://maven.apache.org/) packages. | 11.3+ |
| [NPM Registry](npm_registry/index.md) **(PREMIUM)** | The GitLab NPM Registry enables every project in GitLab to have its own space to store [NPM](https://www.npmjs.com/) packages. | 11.7+ |
+| [NuGet Repository](https://gitlab.com/gitlab-org/gitlab/issues/20050) **(PREMIUM)** | *COMING SOON* The GitLab NuGet Repository will enable every project in GitLab to have its own space to store [NuGet](https://www.nuget.org/) packages. | 12.7 (planned) |
TIP: **Tip:**
Don't you see your package management system supported yet? Consider contributing
to GitLab. This [development documentation](../../development/packages.md) will
-guide you through the process.
+guide you through the process. Or check out how other members of the commmunity
+are adding support for [PHP](https://gitlab.com/gitlab-org/gitlab/merge_requests/17417) or [Terraform](https://gitlab.com/gitlab-org/gitlab/merge_requests/18834).
+
+NOTE: **Note** We are especially interested in adding support for [PyPi](https://gitlab.com/gitlab-org/gitlab/issues/10483), [RubyGems](https://gitlab.com/gitlab-org/gitlab/issues/803), [Debian](https://gitlab.com/gitlab-org/gitlab/issues/5835), and [RPM](https://gitlab.com/gitlab-org/gitlab/issues/5932).
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index d8b59ae63d0..edf299d6fdb 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -156,9 +156,10 @@ a given scope, you will receive a `403 Forbidden!` error.
## Uploading a package with the same version twice
-If you upload a package with a same name and version twice, GitLab will show
-both packages in the UI, but the GitLab NPM Registry will expose the most recent
-one as it supports only one package per version for `npm install`.
+You cannot upload a package with the same name and version twice, unless you
+delete the existing package first. This aligns with npmjs.org's behavior, with
+the exception that npmjs.org does not allow users to ever publish the same version
+more than once, even if it has been deleted.
## Troubleshooting
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 61bc66a6a69..88dc8cbcabe 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -8,9 +8,9 @@ Quick actions are textual shortcuts for common actions on issues, epics, merge r
and commits that are usually done by clicking buttons or dropdowns in GitLab's UI.
You can enter these commands while creating a new issue or merge request, or
in comments of issues, epics, merge requests, and commits. Each command should be
-on a separate line in order to be properly detected and executed. Once executed,
+on a separate line in order to be properly detected and executed.
-> From [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/26672), an alert is displayed when a quick action is successfully applied.
+> From [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/26672), once an action is executed, an alert is displayed when a quick action is successfully applied.
## Quick Actions for issues, merge requests and epics
diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
index 4993d22d400..424dff4e1ae 100644
--- a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
@@ -6,7 +6,7 @@
variables:
DS_ANALYZER_IMAGE_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
- DS_DEFAULT_ANALYZERS: "gemnasium, retire.js, gemnasium-python, gemnasium-maven, bundler-audit"
+ DS_DEFAULT_ANALYZERS: "bundler-audit, retire.js, gemnasium, gemnasium-maven, gemnasium-python"
DS_MAJOR_VERSION: 2
DS_DISABLE_DIND: "false"
diff --git a/lib/gitlab/kubernetes/kubectl_cmd.rb b/lib/gitlab/kubernetes/kubectl_cmd.rb
index 981eb5681dc..e8fde28b44d 100644
--- a/lib/gitlab/kubernetes/kubectl_cmd.rb
+++ b/lib/gitlab/kubernetes/kubectl_cmd.rb
@@ -13,6 +13,16 @@ module Gitlab
%w(kubectl apply -f).concat([filename], args).shelljoin
end
+
+ def delete_crds_from_group(group)
+ api_resources_args = %w(-o name --api-group).push(group)
+
+ api_resources(*api_resources_args) + " | xargs " + delete('--ignore-not-found', 'crd')
+ end
+
+ def api_resources(*args)
+ %w(kubectl api-resources).concat(args).shelljoin
+ end
end
end
end
diff --git a/lib/sentry/client.rb b/lib/sentry/client.rb
index e4428f6fff2..3aa3eb45d85 100644
--- a/lib/sentry/client.rb
+++ b/lib/sentry/client.rb
@@ -5,6 +5,14 @@ module Sentry
Error = Class.new(StandardError)
MissingKeysError = Class.new(StandardError)
ResponseInvalidSizeError = Class.new(StandardError)
+ BadRequestError = Class.new(StandardError)
+
+ SENTRY_API_SORT_VALUE_MAP = {
+ # <accepted_by_client> => <accepted_by_sentry_api>
+ 'frequency' => 'freq',
+ 'first_seen' => 'new',
+ 'last_seen' => nil
+ }.freeze
attr_accessor :url, :token
@@ -25,12 +33,8 @@ module Sentry
map_to_event(latest_event)
end
- def list_issues(issue_status:, limit:, search_term: '')
- issues = get_issues(
- issue_status: issue_status,
- limit: limit,
- search_term: search_term
- )
+ def list_issues(**keyword_args)
+ issues = get_issues(keyword_args)
validate_size(issues)
@@ -52,14 +56,14 @@ module Sentry
def validate_size(issues)
return if Gitlab::Utils::DeepSize.new(issues).valid?
- raise Client::ResponseInvalidSizeError, "Sentry API response is too big. Limit is #{Gitlab::Utils::DeepSize.human_default_max_size}."
+ raise ResponseInvalidSizeError, "Sentry API response is too big. Limit is #{Gitlab::Utils::DeepSize.human_default_max_size}."
end
def handle_mapping_exceptions(&block)
yield
rescue KeyError => e
Gitlab::Sentry.track_acceptable_exception(e)
- raise Client::MissingKeysError, "Sentry API response is missing keys. #{e.message}"
+ raise MissingKeysError, "Sentry API response is missing keys. #{e.message}"
end
def request_params
@@ -78,13 +82,25 @@ module Sentry
handle_response(response)
end
- def get_issues(issue_status:, limit:, search_term: '')
- query = "is:#{issue_status} #{search_term}".strip
+ def get_issues(**keyword_args)
+ http_get(
+ issues_api_url,
+ query: list_issue_sentry_query(keyword_args)
+ )
+ end
+
+ def list_issue_sentry_query(issue_status:, limit:, sort: nil, search_term: '')
+ unless SENTRY_API_SORT_VALUE_MAP.key?(sort)
+ raise BadRequestError, 'Invalid value for sort param'
+ end
+
+ query_params = {
+ query: "is:#{issue_status} #{search_term}".strip,
+ limit: limit,
+ sort: SENTRY_API_SORT_VALUE_MAP[sort]
+ }
- http_get(issues_api_url, query: {
- query: query,
- limit: limit
- })
+ query_params.compact
end
def get_issue(issue_id:)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ce9bc053b98..7aaa4a21ced 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5692,9 +5692,6 @@ msgstr ""
msgid "Deployed"
msgstr ""
-msgid "Deployed %{deployedSince}"
-msgstr ""
-
msgid "Deployed to"
msgstr ""
@@ -10390,6 +10387,9 @@ msgstr ""
msgid "Logs|To see the pod logs, deploy your code to an environment."
msgstr ""
+msgid "MB"
+msgstr ""
+
msgid "MD5"
msgstr ""
diff --git a/qa/qa/resource/deploy_token.rb b/qa/qa/resource/deploy_token.rb
index f0f0da27412..f97e76cc322 100644
--- a/qa/qa/resource/deploy_token.rb
+++ b/qa/qa/resource/deploy_token.rb
@@ -6,16 +6,16 @@ module QA
attr_accessor :name, :expires_at
attribute :username do
- Page::Project::Settings::Repository.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.expand_deploy_tokens do |token|
+ Page::Project::Settings::Repository.perform do |repository_page|
+ repository_page.expand_deploy_tokens do |token|
token.token_username
end
end
end
attribute :password do
- Page::Project::Settings::Repository.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.expand_deploy_tokens do |token|
+ Page::Project::Settings::Repository.perform do |repository_page|
+ repository_page.expand_deploy_tokens do |token|
token.token_password
end
end
diff --git a/qa/qa/resource/file.rb b/qa/qa/resource/file.rb
index a870e7d5d26..76c4c71c48d 100644
--- a/qa/qa/resource/file.rb
+++ b/qa/qa/resource/file.rb
@@ -27,11 +27,11 @@ module QA
Page::Project::Show.perform(&:create_first_new_file!)
- Page::File::Form.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.add_name(@name)
- page.add_content(@content)
- page.add_commit_message(@commit_message)
- page.commit_changes
+ Page::File::Form.perform do |form|
+ form.add_name(@name)
+ form.add_content(@content)
+ form.add_commit_message(@commit_message)
+ form.commit_changes
end
end
diff --git a/qa/qa/resource/fork.rb b/qa/qa/resource/fork.rb
index d203e8eb264..73f1b0b9695 100644
--- a/qa/qa/resource/fork.rb
+++ b/qa/qa/resource/fork.rb
@@ -41,8 +41,8 @@ module QA
fork_new.choose_namespace(user.name)
end
- Page::Layout::Banner.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.has_notice?('The project was successfully forked.')
+ Page::Layout::Banner.perform do |banner|
+ banner.has_notice?('The project was successfully forked.')
end
populate(:project)
diff --git a/qa/qa/resource/kubernetes_cluster.rb b/qa/qa/resource/kubernetes_cluster.rb
index 6778094cee4..1930e0465b2 100644
--- a/qa/qa/resource/kubernetes_cluster.rb
+++ b/qa/qa/resource/kubernetes_cluster.rb
@@ -24,36 +24,36 @@ module QA
Page::Project::Operations::Kubernetes::Add.perform(
&:add_existing_cluster)
- Page::Project::Operations::Kubernetes::AddExisting.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.set_cluster_name(@cluster.cluster_name)
- page.set_api_url(@cluster.api_url)
- page.set_ca_certificate(@cluster.ca_certificate)
- page.set_token(@cluster.token)
- page.uncheck_rbac! unless @cluster.rbac
- page.add_cluster!
+ Page::Project::Operations::Kubernetes::AddExisting.perform do |cluster_page|
+ cluster_page.set_cluster_name(@cluster.cluster_name)
+ cluster_page.set_api_url(@cluster.api_url)
+ cluster_page.set_ca_certificate(@cluster.ca_certificate)
+ cluster_page.set_token(@cluster.token)
+ cluster_page.uncheck_rbac! unless @cluster.rbac
+ cluster_page.add_cluster!
end
if @install_helm_tiller
- Page::Project::Operations::Kubernetes::Show.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
+ Page::Project::Operations::Kubernetes::Show.perform do |show|
# We must wait a few seconds for permissions to be set up correctly for new cluster
sleep 10
# Helm must be installed before everything else
- page.install!(:helm)
- page.await_installed(:helm)
+ show.install!(:helm)
+ show.await_installed(:helm)
- page.install!(:ingress) if @install_ingress
- page.install!(:prometheus) if @install_prometheus
- page.install!(:runner) if @install_runner
+ show.install!(:ingress) if @install_ingress
+ show.install!(:prometheus) if @install_prometheus
+ show.install!(:runner) if @install_runner
- page.await_installed(:ingress) if @install_ingress
- page.await_installed(:prometheus) if @install_prometheus
- page.await_installed(:runner) if @install_runner
+ show.await_installed(:ingress) if @install_ingress
+ show.await_installed(:prometheus) if @install_prometheus
+ show.await_installed(:runner) if @install_runner
if @install_ingress
populate(:ingress_ip)
- page.set_domain("#{ingress_ip}.nip.io")
- page.save_domain
+ show.set_domain("#{ingress_ip}.nip.io")
+ show.save_domain
end
end
end
diff --git a/qa/qa/resource/label.rb b/qa/qa/resource/label.rb
index a9177ef3df6..6b0b0184130 100644
--- a/qa/qa/resource/label.rb
+++ b/qa/qa/resource/label.rb
@@ -28,11 +28,11 @@ module QA
Page::Project::Menu.perform(&:go_to_labels)
Page::Label::Index.perform(&:click_new_label_button)
- Page::Label::New.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.fill_title(@title)
- page.fill_description(@description)
- page.fill_color(@color)
- page.click_label_create_button
+ Page::Label::New.perform do |new_page|
+ new_page.fill_title(@title)
+ new_page.fill_description(@description)
+ new_page.fill_color(@color)
+ new_page.click_label_create_button
end
end
diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb
index 1a6de8de456..24fb96a20a2 100644
--- a/qa/qa/resource/merge_request.rb
+++ b/qa/qa/resource/merge_request.rb
@@ -65,17 +65,17 @@ module QA
project.visit!
Page::Project::Show.perform(&:new_merge_request)
- Page::MergeRequest::New.perform do |new|
- new.fill_title(@title)
- new.fill_description(@description)
- new.choose_milestone(@milestone) if @milestone
- new.assign_to_me if @assignee == 'me'
+ Page::MergeRequest::New.perform do |new_page|
+ new_page.fill_title(@title)
+ new_page.fill_description(@description)
+ new_page.choose_milestone(@milestone) if @milestone
+ new_page.assign_to_me if @assignee == 'me'
labels.each do |label|
- new.select_label(label)
+ new_page.select_label(label)
end
- new.add_approval_rules(approval_rules) if approval_rules
+ new_page.add_approval_rules(approval_rules) if approval_rules
- new.create_merge_request
+ new_page.create_merge_request
end
end
diff --git a/qa/qa/resource/personal_access_token.rb b/qa/qa/resource/personal_access_token.rb
index f5bebd25202..3b61b3f337c 100644
--- a/qa/qa/resource/personal_access_token.rb
+++ b/qa/qa/resource/personal_access_token.rb
@@ -16,10 +16,10 @@ module QA
Page::Main::Menu.perform(&:click_settings_link)
Page::Profile::Menu.perform(&:click_access_tokens)
- Page::Profile::PersonalAccessTokens.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.fill_token_name(name || 'api-test-token')
- page.check_api
- page.click_create_token_button
+ Page::Profile::PersonalAccessTokens.perform do |token_page|
+ token_page.fill_token_name(name || 'api-test-token')
+ token_page.check_api
+ token_page.click_create_token_button
end
end
end
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
index 3bebe2aaeda..b2b62184837 100644
--- a/qa/qa/resource/project.rb
+++ b/qa/qa/resource/project.rb
@@ -32,14 +32,14 @@ module QA
end
attribute :repository_ssh_location do
- Page::Project::Show.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.repository_clone_ssh_location
+ Page::Project::Show.perform do |show|
+ show.repository_clone_ssh_location
end
end
attribute :repository_http_location do
- Page::Project::Show.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.repository_clone_http_location
+ Page::Project::Show.perform do |show|
+ show.repository_clone_http_location
end
end
@@ -62,13 +62,13 @@ module QA
Page::Group::Show.perform(&:go_to_new_project)
end
- Page::Project::New.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.choose_test_namespace
- page.choose_name(@name)
- page.add_description(@description)
- page.set_visibility(@visibility)
- page.enable_initialize_with_readme if @initialize_with_readme
- page.create_new_project
+ Page::Project::New.perform do |new_page|
+ new_page.choose_test_namespace
+ new_page.choose_name(@name)
+ new_page.add_description(@description)
+ new_page.set_visibility(@visibility)
+ new_page.enable_initialize_with_readme if @initialize_with_readme
+ new_page.create_new_project
end
end
diff --git a/qa/qa/resource/project_imported_from_github.rb b/qa/qa/resource/project_imported_from_github.rb
index e4a9a8bcd3d..3e25235e6b8 100644
--- a/qa/qa/resource/project_imported_from_github.rb
+++ b/qa/qa/resource/project_imported_from_github.rb
@@ -17,18 +17,14 @@ module QA
Page::Group::Show.perform(&:go_to_new_project)
- Page::Project::New.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.click_import_project
- end
+ Page::Project::New.perform(&:click_import_project)
- Page::Project::New.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.click_github_link
- end
+ Page::Project::New.perform(&:click_github_link)
- Page::Project::Import::Github.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.add_personal_access_token(@personal_access_token)
- page.list_repos
- page.import!(@github_repository_path, @name)
+ Page::Project::Import::Github.perform do |import_page|
+ import_page.add_personal_access_token(@personal_access_token)
+ import_page.list_repos
+ import_page.import!(@github_repository_path, @name)
end
end
end
diff --git a/qa/qa/resource/project_milestone.rb b/qa/qa/resource/project_milestone.rb
index 39077b64a22..11aad4ae65f 100644
--- a/qa/qa/resource/project_milestone.rb
+++ b/qa/qa/resource/project_milestone.rb
@@ -18,9 +18,9 @@ module QA
def fabricate!
project.visit!
- Page::Project::Menu.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.click_issues
- page.click_milestones
+ Page::Project::Menu.perform do |menu|
+ menu.click_issues
+ menu.click_milestones
end
Page::Project::Milestone::Index.perform(&:click_new_milestone)
diff --git a/qa/qa/resource/snippet.rb b/qa/qa/resource/snippet.rb
index 23c17fdb32a..39be5e5cb7d 100644
--- a/qa/qa/resource/snippet.rb
+++ b/qa/qa/resource/snippet.rb
@@ -16,13 +16,13 @@ module QA
def fabricate!
Page::Dashboard::Snippet::Index.perform(&:go_to_new_snippet_page)
- Page::Dashboard::Snippet::New.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.fill_title(@title)
- page.fill_description(@description)
- page.set_visibility(@visibility)
- page.fill_file_name(@file_name)
- page.fill_file_content(@file_content)
- page.click_create_snippet_button
+ Page::Dashboard::Snippet::New.perform do |new_page|
+ new_page.fill_title(@title)
+ new_page.fill_description(@description)
+ new_page.set_visibility(@visibility)
+ new_page.fill_file_name(@file_name)
+ new_page.fill_file_content(@file_content)
+ new_page.click_create_snippet_button
end
end
end
diff --git a/qa/qa/resource/ssh_key.rb b/qa/qa/resource/ssh_key.rb
index 9b6494c11bc..c140cb9ca62 100644
--- a/qa/qa/resource/ssh_key.rb
+++ b/qa/qa/resource/ssh_key.rb
@@ -17,8 +17,8 @@ module QA
Page::Main::Menu.perform(&:click_settings_link)
Page::Profile::Menu.perform(&:click_ssh_keys)
- Page::Profile::SSHKeys.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.add_key(public_key, title)
+ Page::Profile::SSHKeys.perform do |profile_page|
+ profile_page.add_key(public_key, title)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/create_group_with_mattermost_team_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/create_group_with_mattermost_team_spec.rb
index de33349a8b2..66c56c86fc8 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/group/create_group_with_mattermost_team_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/group/create_group_with_mattermost_team_spec.rb
@@ -8,10 +8,10 @@ module QA
Page::Main::Login.perform(&:sign_in_using_credentials)
Page::Main::Menu.perform(&:go_to_groups)
- Page::Dashboard::Groups.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.click_new_group
+ Page::Dashboard::Groups.perform do |groups|
+ groups.click_new_group
- expect(page).to have_content(
+ expect(groups).to have_content(
/Create a Mattermost team for this group/
)
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb
index babe6f7741f..e66c12c1301 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb
@@ -11,8 +11,8 @@ module QA
Runtime::Browser.visit(:mattermost, Page::Mattermost::Login)
Page::Mattermost::Login.perform(&:sign_in_using_oauth)
- Page::Mattermost::Main.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- expect(page).to have_content(/(Welcome to: Mattermost|Logout GitLab Mattermost)/)
+ Page::Mattermost::Main.perform do |mattermost|
+ expect(mattermost).to have_content(/(Welcome to: Mattermost|Logout GitLab Mattermost)/)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
index 07298b8a9be..abb46463ed2 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
@@ -15,8 +15,8 @@ module QA
project.visit!
Page::Project::Menu.perform(&:go_to_members_settings)
- Page::Project::Settings::Members.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.add_member(user.username)
+ Page::Project::Settings::Members.perform do |members|
+ members.add_member(user.username)
end
expect(page).to have_content(/@#{user.username}(\n| )?Given access/)
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
index 69389672a6d..494108dbefc 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
@@ -27,8 +27,8 @@ module QA
project.visit!
Page::Project::Show.perform(&:go_to_members_settings)
- Page::Project::Settings::Members.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.add_member(user.username)
+ Page::Project::Settings::Members.perform do |members|
+ members.add_member(user.username)
end
issue = Resource::Issue.fabricate_via_api! do |issue|
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
index e15afd1f576..400679365be 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
@@ -23,12 +23,12 @@ module QA
it 'user sees issue suggestions when creating a new issue' do
Page::Project::Show.perform(&:go_to_new_issue)
- Page::Project::Issue::New.perform do |new|
- new.add_title("issue")
- expect(new).to have_content(issue_title)
+ Page::Project::Issue::New.perform do |new_page|
+ new_page.add_title("issue")
+ expect(new_page).to have_content(issue_title)
- new.add_title("Issue Board")
- expect(new).not_to have_content(issue_title)
+ new_page.add_title("Issue Board")
+ expect(new_page).not_to have_content(issue_title)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
index 318adc3c272..71b9971a0af 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
@@ -50,10 +50,10 @@ module QA
@project.visit!
Page::Project::Show.perform(&:open_web_ide!)
- Page::Project::WebIDE::Edit.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.create_new_file_from_template template[:file_name], template[:name]
+ Page::Project::WebIDE::Edit.perform do |ide|
+ ide.create_new_file_from_template template[:file_name], template[:name]
- expect(page.has_file?(template[:file_name])).to be_truthy
+ expect(ide.has_file?(template[:file_name])).to be_truthy
end
expect(page).to have_button('Undo')
diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb
index 2c3f2c86c23..770d66141eb 100644
--- a/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb
@@ -16,9 +16,9 @@ module QA
validate_content('My First Wiki Content')
Page::Project::Wiki::Edit.perform(&:click_edit)
- Page::Project::Wiki::New.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.set_content("My Second Wiki Content")
- page.save_changes
+ Page::Project::Wiki::New.perform do |wiki|
+ wiki.set_content("My Second Wiki Content")
+ wiki.save_changes
end
validate_content('My Second Wiki Content')
diff --git a/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb b/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb
index 167a68dab00..757d047b728 100644
--- a/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb
+++ b/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb
@@ -23,10 +23,10 @@ module QA
issue.title = 'Performance bar test'
end
- Page::Layout::PerformanceBar.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- expect(page).to have_performance_bar
- expect(page).to have_detailed_metrics
- expect(page).to have_request_for('realtime_changes') # Always requested on issue pages
+ Page::Layout::PerformanceBar.perform do |bar_component|
+ expect(bar_component).to have_performance_bar
+ expect(bar_component).to have_detailed_metrics
+ expect(bar_component).to have_request_for('realtime_changes') # Always requested on issue pages
end
end
end
diff --git a/scripts/review_apps/automated_cleanup.rb b/scripts/review_apps/automated_cleanup.rb
index 01bf149e76e..cf7c96cb29d 100755
--- a/scripts/review_apps/automated_cleanup.rb
+++ b/scripts/review_apps/automated_cleanup.rb
@@ -75,8 +75,8 @@ class AutomatedCleanup
deployed_at = Time.parse(last_deploy)
if deployed_at < delete_threshold
- environment = delete_environment(environment, deployment)
- if environment
+ deleted_environment = delete_environment(environment, deployment)
+ if deleted_environment
release = Quality::HelmClient::Release.new(environment.slug, 1, deployed_at.to_s, nil, nil, review_apps_namespace)
releases_to_delete << release
end
diff --git a/spec/controllers/projects/error_tracking_controller_spec.rb b/spec/controllers/projects/error_tracking_controller_spec.rb
index 88365e264f4..45f3188baae 100644
--- a/spec/controllers/projects/error_tracking_controller_spec.rb
+++ b/spec/controllers/projects/error_tracking_controller_spec.rb
@@ -48,20 +48,17 @@ describe Projects::ErrorTrackingController do
describe 'format json' do
let(:list_issues_service) { spy(:list_issues_service) }
let(:external_url) { 'http://example.com' }
- let(:search_term) do
- ActionController::Parameters.new(
- search_term: 'something'
- ).permit!
- end
context 'no data' do
- let(:search_term) do
+ let(:params) { project_params(format: :json) }
+
+ let(:permitted_params) do
ActionController::Parameters.new({}).permit!
end
before do
expect(ErrorTracking::ListIssuesService)
- .to receive(:new).with(project, user, search_term)
+ .to receive(:new).with(project, user, permitted_params)
.and_return(list_issues_service)
expect(list_issues_service).to receive(:execute)
@@ -75,10 +72,16 @@ describe Projects::ErrorTrackingController do
end
end
- context 'with a search_term param' do
+ context 'with a search_term and sort params' do
+ let(:params) { project_params(format: :json, search_term: 'something', sort: 'last_seen') }
+
+ let(:permitted_params) do
+ ActionController::Parameters.new(search_term: 'something', sort: 'last_seen').permit!
+ end
+
before do
expect(ErrorTracking::ListIssuesService)
- .to receive(:new).with(project, user, search_term)
+ .to receive(:new).with(project, user, permitted_params)
.and_return(list_issues_service)
end
@@ -93,7 +96,7 @@ describe Projects::ErrorTrackingController do
let(:error) { build(:error_tracking_error) }
it 'returns a list of errors' do
- get :index, params: project_params(format: :json, search_term: 'something')
+ get :index, params: params
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('error_tracking/index')
@@ -103,7 +106,7 @@ describe Projects::ErrorTrackingController do
end
end
- context 'without a search_term param' do
+ context 'without params' do
before do
expect(ErrorTracking::ListIssuesService)
.to receive(:new).with(project, user, {})
diff --git a/spec/frontend/environments/environment_rollback_spec.js b/spec/frontend/environments/environment_rollback_spec.js
index fb62a096c3d..33e04f5eb29 100644
--- a/spec/frontend/environments/environment_rollback_spec.js
+++ b/spec/frontend/environments/environment_rollback_spec.js
@@ -13,6 +13,8 @@ describe('Rollback Component', () => {
isLastDeployment: true,
environment: {},
},
+ attachToDocument: true,
+ sync: false,
});
expect(wrapper.element).toHaveSpriteIcon('repeat');
@@ -25,6 +27,8 @@ describe('Rollback Component', () => {
isLastDeployment: false,
environment: {},
},
+ attachToDocument: true,
+ sync: false,
});
expect(wrapper.element).toHaveSpriteIcon('redo');
diff --git a/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap
new file mode 100644
index 00000000000..a7f666ff56d
--- /dev/null
+++ b/spec/frontend/vue_shared/components/__snapshots__/memory_graph_spec.js.snap
@@ -0,0 +1,15 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MemoryGraph Render chart should draw container with chart 1`] = `
+<div
+ class="memory-graph-container p-1"
+ style="width: 100px;"
+>
+ <glsparklinechart-stub
+ data="Nov 12 2019 19:17:33,2.87,Nov 12 2019 19:18:33,2.78,Nov 12 2019 19:19:33,2.78,Nov 12 2019 19:20:33,3.01"
+ height="25"
+ tooltiplabel="MB"
+ variant="gray900"
+ />
+</div>
+`;
diff --git a/spec/frontend/vue_shared/components/memory_graph_spec.js b/spec/frontend/vue_shared/components/memory_graph_spec.js
new file mode 100644
index 00000000000..93f32f0b278
--- /dev/null
+++ b/spec/frontend/vue_shared/components/memory_graph_spec.js
@@ -0,0 +1,53 @@
+import Vue from 'vue';
+import { shallowMount } from '@vue/test-utils';
+import MemoryGraph from '~/vue_shared/components/memory_graph.vue';
+import { GlSparklineChart } from '@gitlab/ui/dist/charts';
+
+describe('MemoryGraph', () => {
+ const Component = Vue.extend(MemoryGraph);
+ let wrapper;
+ const metrics = [
+ [1573586253.853, '2.87'],
+ [1573586313.853, '2.77734375'],
+ [1573586373.853, '2.77734375'],
+ [1573586433.853, '3.0066964285714284'],
+ ];
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ beforeEach(() => {
+ wrapper = shallowMount(Component, {
+ propsData: {
+ metrics,
+ width: 100,
+ height: 25,
+ },
+ });
+ });
+
+ describe('chartData', () => {
+ it('should calculate chartData', () => {
+ expect(wrapper.vm.chartData.length).toEqual(metrics.length);
+ });
+
+ it('should format date & MB values', () => {
+ const formattedData = [
+ ['Nov 12 2019 19:17:33', '2.87'],
+ ['Nov 12 2019 19:18:33', '2.78'],
+ ['Nov 12 2019 19:19:33', '2.78'],
+ ['Nov 12 2019 19:20:33', '3.01'],
+ ];
+ expect(wrapper.vm.chartData).toEqual(formattedData);
+ });
+ });
+
+ describe('Render chart', () => {
+ it('should draw container with chart', () => {
+ expect(wrapper.element).toMatchSnapshot();
+ expect(wrapper.find('.memory-graph-container').exists()).toBe(true);
+ expect(wrapper.find(GlSparklineChart).exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/graphql/mutations/todos/restore_spec.rb b/spec/graphql/mutations/todos/restore_spec.rb
new file mode 100644
index 00000000000..76a2d4ffffd
--- /dev/null
+++ b/spec/graphql/mutations/todos/restore_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Mutations::Todos::Restore do
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:author) { create(:user) }
+ let_it_be(:other_user) { create(:user) }
+
+ let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :done) }
+ let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :pending) }
+
+ let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :done) }
+
+ let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }) }
+
+ describe '#resolve' do
+ it 'restores a single todo' do
+ result = restore_mutation(todo1)
+
+ expect(todo1.reload.state).to eq('pending')
+ expect(todo2.reload.state).to eq('pending')
+ expect(other_user_todo.reload.state).to eq('done')
+
+ todo = result[:todo]
+ expect(todo.id).to eq(todo1.id)
+ expect(todo.state).to eq('pending')
+ end
+
+ it 'handles a todo which is already pending as expected' do
+ result = restore_mutation(todo2)
+
+ expect(todo1.reload.state).to eq('done')
+ expect(todo2.reload.state).to eq('pending')
+ expect(other_user_todo.reload.state).to eq('done')
+
+ todo = result[:todo]
+ expect(todo.id).to eq(todo2.id)
+ expect(todo.state).to eq('pending')
+ end
+
+ it 'ignores requests for todos which do not belong to the current user' do
+ expect { restore_mutation(other_user_todo) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+
+ expect(todo1.reload.state).to eq('done')
+ expect(todo2.reload.state).to eq('pending')
+ expect(other_user_todo.reload.state).to eq('done')
+ end
+
+ it 'ignores invalid GIDs' do
+ expect { mutation.resolve(id: 'invalid_gid') }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+
+ expect(todo1.reload.state).to eq('done')
+ expect(todo2.reload.state).to eq('pending')
+ expect(other_user_todo.reload.state).to eq('done')
+ end
+ end
+
+ def restore_mutation(todo)
+ mutation.resolve(id: global_id_of(todo))
+ end
+
+ def global_id_of(todo)
+ todo.to_global_id.to_s
+ end
+end
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
index 4baaea9745a..99de320ca2b 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
@@ -185,6 +185,7 @@ describe('MemoryUsage', () => {
vm.loadingMetrics = false;
vm.hasMetrics = true;
vm.loadFailed = false;
+ vm.memoryMetrics = metricsMockData.metrics.memory_values[0].values;
Vue.nextTick(() => {
expect(el.querySelector('.memory-graph-container')).toBeDefined();
diff --git a/spec/javascripts/vue_shared/components/memory_graph_spec.js b/spec/javascripts/vue_shared/components/memory_graph_spec.js
deleted file mode 100644
index 78c3ae3ddb3..00000000000
--- a/spec/javascripts/vue_shared/components/memory_graph_spec.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import Vue from 'vue';
-import MemoryGraph from '~/vue_shared/components/memory_graph.vue';
-import { mockMetrics, mockMedian, mockMedianIndex } from './mock_data';
-
-const defaultHeight = '25';
-const defaultWidth = '100';
-
-const createComponent = () => {
- const Component = Vue.extend(MemoryGraph);
-
- return new Component({
- el: document.createElement('div'),
- propsData: {
- metrics: [],
- deploymentTime: 0,
- width: '',
- height: '',
- pathD: '',
- pathViewBox: '',
- dotX: '',
- dotY: '',
- },
- });
-};
-
-describe('MemoryGraph', () => {
- let vm;
- let el;
-
- beforeEach(() => {
- vm = createComponent();
- el = vm.$el;
- });
-
- describe('data', () => {
- it('should have default data', () => {
- const data = MemoryGraph.data();
- const dataValidator = (dataItem, expectedType, defaultVal) => {
- expect(typeof dataItem).toBe(expectedType);
- expect(dataItem).toBe(defaultVal);
- };
-
- dataValidator(data.pathD, 'string', '');
- dataValidator(data.pathViewBox, 'string', '');
- dataValidator(data.dotX, 'string', '');
- dataValidator(data.dotY, 'string', '');
- });
- });
-
- describe('computed', () => {
- describe('getFormattedMedian', () => {
- it('should show human readable median value based on provided median timestamp', () => {
- vm.deploymentTime = mockMedian;
- const formattedMedian = vm.getFormattedMedian;
-
- expect(formattedMedian.indexOf('Deployed')).toBeGreaterThan(-1);
- expect(formattedMedian.indexOf('ago')).toBeGreaterThan(-1);
- });
- });
- });
-
- describe('methods', () => {
- describe('getMedianMetricIndex', () => {
- it('should return index of closest metric timestamp to that of median', () => {
- const matchingIndex = vm.getMedianMetricIndex(mockMedian, mockMetrics);
-
- expect(matchingIndex).toBe(mockMedianIndex);
- });
- });
-
- describe('getGraphPlotValues', () => {
- it('should return Object containing values to plot graph', () => {
- const plotValues = vm.getGraphPlotValues(mockMedian, mockMetrics);
-
- expect(plotValues.pathD).toBeDefined();
- expect(Array.isArray(plotValues.pathD)).toBeTruthy();
-
- expect(plotValues.pathViewBox).toBeDefined();
- expect(typeof plotValues.pathViewBox).toBe('object');
-
- expect(plotValues.dotX).toBeDefined();
- expect(typeof plotValues.dotX).toBe('number');
-
- expect(plotValues.dotY).toBeDefined();
- expect(typeof plotValues.dotY).toBe('number');
- });
- });
- });
-
- describe('template', () => {
- it('should render template elements correctly', () => {
- expect(el.classList.contains('memory-graph-container')).toBeTruthy();
- expect(el.querySelector('svg')).toBeDefined();
- });
-
- it('should render graph when renderGraph is called internally', done => {
- const { pathD, pathViewBox, dotX, dotY } = vm.getGraphPlotValues(mockMedian, mockMetrics);
- vm.height = defaultHeight;
- vm.width = defaultWidth;
- vm.pathD = `M ${pathD}`;
- vm.pathViewBox = `0 0 ${pathViewBox.lineWidth} ${pathViewBox.diff}`;
- vm.dotX = dotX;
- vm.dotY = dotY;
-
- Vue.nextTick(() => {
- const svgEl = el.querySelector('svg');
-
- expect(svgEl).toBeDefined();
- expect(svgEl.getAttribute('height')).toBe(defaultHeight);
- expect(svgEl.getAttribute('width')).toBe(defaultWidth);
-
- const pathEl = el.querySelector('path');
-
- expect(pathEl).toBeDefined();
- expect(pathEl.getAttribute('d')).toBe(`M ${pathD}`);
- expect(pathEl.getAttribute('viewBox')).toBe(
- `0 0 ${pathViewBox.lineWidth} ${pathViewBox.diff}`,
- );
-
- const circleEl = el.querySelector('circle');
-
- expect(circleEl).toBeDefined();
- expect(circleEl.getAttribute('r')).toBe('1.5');
- expect(circleEl.getAttribute('transform')).toBe('translate(0 -1)');
- expect(circleEl.getAttribute('cx')).toBe(`${dotX}`);
- expect(circleEl.getAttribute('cy')).toBe(`${dotY}`);
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/vue_shared/components/mock_data.js b/spec/javascripts/vue_shared/components/mock_data.js
deleted file mode 100644
index 15b56c58c33..00000000000
--- a/spec/javascripts/vue_shared/components/mock_data.js
+++ /dev/null
@@ -1,67 +0,0 @@
-export const mockMetrics = [
- [1493716685, '4.30859375'],
- [1493716745, '4.30859375'],
- [1493716805, '4.30859375'],
- [1493716865, '4.30859375'],
- [1493716925, '4.30859375'],
- [1493716985, '4.30859375'],
- [1493717045, '4.30859375'],
- [1493717105, '4.30859375'],
- [1493717165, '4.30859375'],
- [1493717225, '4.30859375'],
- [1493717285, '4.30859375'],
- [1493717345, '4.30859375'],
- [1493717405, '4.30859375'],
- [1493717465, '4.30859375'],
- [1493717525, '4.30859375'],
- [1493717585, '4.30859375'],
- [1493717645, '4.30859375'],
- [1493717705, '4.30859375'],
- [1493717765, '4.30859375'],
- [1493717825, '4.30859375'],
- [1493717885, '4.30859375'],
- [1493717945, '4.30859375'],
- [1493718005, '4.30859375'],
- [1493718065, '4.30859375'],
- [1493718125, '4.30859375'],
- [1493718185, '4.30859375'],
- [1493718245, '4.30859375'],
- [1493718305, '4.234375'],
- [1493718365, '4.234375'],
- [1493718425, '4.234375'],
- [1493718485, '4.234375'],
- [1493718545, '4.243489583333333'],
- [1493718605, '4.2109375'],
- [1493718665, '4.2109375'],
- [1493718725, '4.2109375'],
- [1493718785, '4.26171875'],
- [1493718845, '4.26171875'],
- [1493718905, '4.26171875'],
- [1493718965, '4.26171875'],
- [1493719025, '4.26171875'],
- [1493719085, '4.26171875'],
- [1493719145, '4.26171875'],
- [1493719205, '4.26171875'],
- [1493719265, '4.26171875'],
- [1493719325, '4.26171875'],
- [1493719385, '4.26171875'],
- [1493719445, '4.26171875'],
- [1493719505, '4.26171875'],
- [1493719565, '4.26171875'],
- [1493719625, '4.26171875'],
- [1493719685, '4.26171875'],
- [1493719745, '4.26171875'],
- [1493719805, '4.26171875'],
- [1493719865, '4.26171875'],
- [1493719925, '4.26171875'],
- [1493719985, '4.26171875'],
- [1493720045, '4.26171875'],
- [1493720105, '4.26171875'],
- [1493720165, '4.26171875'],
- [1493720225, '4.26171875'],
- [1493720285, '4.26171875'],
-];
-
-export const mockMedian = 1493718485;
-
-export const mockMedianIndex = 30;
diff --git a/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb b/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb
index f24ab5579df..b65d7b9fdc6 100644
--- a/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb
@@ -45,4 +45,20 @@ describe Gitlab::Kubernetes::KubectlCmd do
end
end
end
+
+ describe '.api_resources' do
+ it 'constructs string properly' do
+ expected_command = 'kubectl api-resources -o name --api-group foo'
+
+ expect(described_class.api_resources("-o", "name", "--api-group", "foo")).to eq expected_command
+ end
+ end
+
+ describe '.delete_crds_from_group' do
+ it 'constructs string properly' do
+ expected_command = 'kubectl api-resources -o name --api-group foo | xargs kubectl delete --ignore-not-found crd'
+
+ expect(described_class.delete_crds_from_group("foo")).to eq expected_command
+ end
+ end
end
diff --git a/spec/lib/sentry/client_spec.rb b/spec/lib/sentry/client_spec.rb
index b46fa485f82..ed9da77038f 100644
--- a/spec/lib/sentry/client_spec.rb
+++ b/spec/lib/sentry/client_spec.rb
@@ -5,6 +5,12 @@ require 'spec_helper'
describe Sentry::Client do
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let(:token) { 'test-token' }
+ let(:default_httparty_options) do
+ {
+ follow_redirects: false,
+ headers: { "Authorization" => "Bearer test-token" }
+ }
+ end
let(:issues_sample_response) do
Gitlab::Utils.deep_indifferent_access(
@@ -94,7 +100,7 @@ describe Sentry::Client do
let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
- subject { client.list_issues(issue_status: issue_status, limit: limit, search_term: search_term) }
+ subject { client.list_issues(issue_status: issue_status, limit: limit, search_term: search_term, sort: 'last_seen') }
it_behaves_like 'calls sentry api'
@@ -165,6 +171,35 @@ describe Sentry::Client do
end
end
+ context 'requests with sort parameter in sentry api' do
+ let(:sentry_request_url) do
+ 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/' \
+ 'issues/?limit=20&query=is:unresolved&sort=freq'
+ end
+ let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
+
+ subject { client.list_issues(issue_status: issue_status, limit: limit, sort: 'frequency') }
+
+ it 'calls the sentry api with sort params' do
+ expect(Gitlab::HTTP).to receive(:get).with(
+ URI("#{sentry_url}/issues/"),
+ default_httparty_options.merge(query: { limit: 20, query: "is:unresolved", sort: "freq" })
+ ).and_call_original
+
+ subject
+
+ expect(sentry_api_request).to have_been_requested
+ end
+ end
+
+ context 'with invalid sort params' do
+ subject { client.list_issues(issue_status: issue_status, limit: limit, sort: 'fish') }
+
+ it 'throws an error' do
+ expect { subject }.to raise_error(Sentry::Client::BadRequestError, 'Invalid value for sort param')
+ end
+ end
+
context 'Older sentry versions where keys are not present' do
let(:sentry_api_response) do
issues_sample_response[0...1].map do |issue|
diff --git a/spec/migrations/20191015154408_drop_merge_requests_require_code_owner_approval_from_projects_spec.rb b/spec/migrations/20191015154408_drop_merge_requests_require_code_owner_approval_from_projects_spec.rb
new file mode 100644
index 00000000000..e90d56cfeef
--- /dev/null
+++ b/spec/migrations/20191015154408_drop_merge_requests_require_code_owner_approval_from_projects_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20191015154408_drop_merge_requests_require_code_owner_approval_from_projects.rb')
+
+describe DropMergeRequestsRequireCodeOwnerApprovalFromProjects, :migration do
+ let(:projects_table) { table(:projects) }
+
+ subject(:migration) { described_class.new }
+
+ describe "without running the migration" do
+ it "project_table has a :merge_requests_require_code_owner_approval column" do
+ expect(projects_table.column_names)
+ .to include("merge_requests_require_code_owner_approval")
+ end
+
+ it "project_table has a :projects_requiring_code_owner_approval index" do
+ expect(ActiveRecord::Base.connection.indexes(:projects).collect(&:name))
+ .to include("projects_requiring_code_owner_approval")
+ end
+ end
+
+ describe '#up' do
+ context "without running "
+ before do
+ migrate!
+ end
+
+ it "drops the :merge_requests_require_code_owner_approval column" do
+ expect(projects_table.column_names)
+ .not_to include("merge_requests_require_code_owner_approval")
+ end
+
+ it "drops the :projects_requiring_code_owner_approval index" do
+ expect(ActiveRecord::Base.connection.indexes(:projects).collect(&:name))
+ .not_to include("projects_requiring_code_owner_approval")
+ end
+ end
+
+ describe "#down" do
+ before do
+ migration.up
+ migration.down
+ end
+
+ it "project_table has a :merge_requests_require_code_owner_approval column" do
+ expect(projects_table.column_names)
+ .to include("merge_requests_require_code_owner_approval")
+ end
+
+ it "project_table has a :projects_requiring_code_owner_approval index" do
+ expect(ActiveRecord::Base.connection.indexes(:projects).collect(&:name))
+ .to include("projects_requiring_code_owner_approval")
+ end
+ end
+end
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index 51c8a6bb68d..f1ce447a0d8 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -161,18 +161,19 @@ describe Clusters::Applications::Knative do
end
it "initializes command with all necessary postdelete script" do
- api_resources = YAML.safe_load(File.read(Rails.root.join(Clusters::Applications::Knative::API_RESOURCES_PATH)))
+ api_groups = YAML.safe_load(File.read(Rails.root.join(Clusters::Applications::Knative::API_GROUPS_PATH)))
remove_knative_istio_leftovers_script = [
"kubectl delete --ignore-not-found ns knative-serving",
"kubectl delete --ignore-not-found ns knative-build"
]
- full_delete_commands_size = api_resources.size + remove_knative_istio_leftovers_script.size
+ full_delete_commands_size = api_groups.size + remove_knative_istio_leftovers_script.size
expect(subject.postdelete).to include(*remove_knative_istio_leftovers_script)
expect(subject.postdelete.size).to eq(full_delete_commands_size)
- expect(subject.postdelete[2]).to eq("kubectl delete --ignore-not-found crd #{api_resources[0]}")
+ expect(subject.postdelete[2]).to eq("kubectl api-resources -o name --api-group #{api_groups[0]} | xargs kubectl delete --ignore-not-found crd")
+ expect(subject.postdelete[3]).to eq("kubectl api-resources -o name --api-group #{api_groups[1]} | xargs kubectl delete --ignore-not-found crd")
end
end
diff --git a/spec/requests/api/graphql/mutations/todos/restore_spec.rb b/spec/requests/api/graphql/mutations/todos/restore_spec.rb
new file mode 100644
index 00000000000..faa36c8273a
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/todos/restore_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Restoring Todos' do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:author) { create(:user) }
+ let_it_be(:other_user) { create(:user) }
+
+ let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :done) }
+ let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :pending) }
+
+ let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :done) }
+
+ let(:input) { { id: todo1.to_global_id.to_s } }
+
+ let(:mutation) do
+ graphql_mutation(:todo_restore, input,
+ <<-QL.strip_heredoc
+ clientMutationId
+ errors
+ todo {
+ id
+ state
+ }
+ QL
+ )
+ end
+
+ def mutation_response
+ graphql_mutation_response(:todo_restore)
+ end
+
+ it 'restores a single todo' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(todo1.reload.state).to eq('pending')
+ expect(todo2.reload.state).to eq('pending')
+ expect(other_user_todo.reload.state).to eq('done')
+
+ todo = mutation_response['todo']
+ expect(todo['id']).to eq(todo1.to_global_id.to_s)
+ expect(todo['state']).to eq('pending')
+ end
+
+ context 'when todo is already marked pending' do
+ let(:input) { { id: todo2.to_global_id.to_s } }
+
+ it 'has the expected response' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(todo1.reload.state).to eq('done')
+ expect(todo2.reload.state).to eq('pending')
+ expect(other_user_todo.reload.state).to eq('done')
+
+ todo = mutation_response['todo']
+ expect(todo['id']).to eq(todo2.to_global_id.to_s)
+ expect(todo['state']).to eq('pending')
+ end
+ end
+
+ context 'when todo does not belong to requesting user' do
+ let(:input) { { id: other_user_todo.to_global_id.to_s } }
+ let(:access_error) { 'The resource that you are attempting to access does not exist or you don\'t have permission to perform this action' }
+
+ it 'contains the expected error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ errors = json_response['errors']
+ expect(errors).not_to be_blank
+ expect(errors.first['message']).to eq(access_error)
+
+ expect(todo1.reload.state).to eq('done')
+ expect(todo2.reload.state).to eq('pending')
+ expect(other_user_todo.reload.state).to eq('done')
+ end
+ end
+
+ context 'when using an invalid gid' do
+ let(:input) { { id: 'invalid_gid' } }
+ let(:invalid_gid_error) { 'invalid_gid is not a valid GitLab id.' }
+
+ it 'contains the expected error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ errors = json_response['errors']
+ expect(errors).not_to be_blank
+ expect(errors.first['message']).to eq(invalid_gid_error)
+
+ expect(todo1.reload.state).to eq('done')
+ expect(todo2.reload.state).to eq('pending')
+ expect(other_user_todo.reload.state).to eq('done')
+ end
+ end
+end
diff --git a/spec/services/error_tracking/list_issues_service_spec.rb b/spec/services/error_tracking/list_issues_service_spec.rb
index 8e2abe8e07d..67455190450 100644
--- a/spec/services/error_tracking/list_issues_service_spec.rb
+++ b/spec/services/error_tracking/list_issues_service_spec.rb
@@ -5,12 +5,13 @@ require 'spec_helper'
describe ErrorTracking::ListIssuesService do
set(:user) { create(:user) }
set(:project) { create(:project) }
- let(:params) { { search_term: 'something' } }
+ let(:params) { { search_term: 'something', sort: 'last_seen' } }
let(:list_sentry_issues_args) do
{
issue_status: 'unresolved',
limit: 20,
- search_term: params[:search_term]
+ search_term: params[:search_term],
+ sort: params[:sort]
}
end
diff --git a/spec/support/sidekiq.rb b/spec/support/sidekiq.rb
index 246efedc7e5..a6d6d5fc6e1 100644
--- a/spec/support/sidekiq.rb
+++ b/spec/support/sidekiq.rb
@@ -1,31 +1,5 @@
# frozen_string_literal: true
-require 'sidekiq/testing'
-
-# If Sidekiq::Testing.inline! is used, SQL transactions done inside
-# Sidekiq worker are included in the SQL query limit (in a real
-# deployment sidekiq worker is executed separately). To avoid
-# increasing SQL limit counter, the request is marked as whitelisted
-# during Sidekiq block
-class DisableQueryLimit
- def call(worker_instance, msg, queue)
- transaction = Gitlab::QueryLimiting::Transaction.current
-
- if !transaction.respond_to?(:whitelisted) || transaction.whitelisted
- yield
- else
- transaction.whitelisted = true
- yield
- transaction.whitelisted = false
- end
- end
-end
-
-Sidekiq::Testing.server_middleware do |chain|
- chain.add Gitlab::SidekiqStatus::ServerMiddleware
- chain.add DisableQueryLimit
-end
-
RSpec.configure do |config|
config.around(:each, :sidekiq) do |example|
Sidekiq::Worker.clear_all
diff --git a/spec/support/sidekiq_middleware.rb b/spec/support/sidekiq_middleware.rb
new file mode 100644
index 00000000000..f6694713101
--- /dev/null
+++ b/spec/support/sidekiq_middleware.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'sidekiq/testing'
+
+# If Sidekiq::Testing.inline! is used, SQL transactions done inside
+# Sidekiq worker are included in the SQL query limit (in a real
+# deployment sidekiq worker is executed separately). To avoid
+# increasing SQL limit counter, the request is marked as whitelisted
+# during Sidekiq block
+class DisableQueryLimit
+ def call(worker_instance, msg, queue)
+ transaction = Gitlab::QueryLimiting::Transaction.current
+
+ if !transaction.respond_to?(:whitelisted) || transaction.whitelisted
+ yield
+ else
+ transaction.whitelisted = true
+ yield
+ transaction.whitelisted = false
+ end
+ end
+end
+
+Sidekiq::Testing.server_middleware do |chain|
+ chain.add Gitlab::SidekiqStatus::ServerMiddleware
+ chain.add DisableQueryLimit
+end