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

Add latest changes from gitlab-org/gitlab@master

parent 1f1bdf54
No related branches found
No related tags found
No related merge requests found
Showing
with 222 additions and 43 deletions
Loading
Loading
@@ -10,8 +10,8 @@ schedule:package-and-qa:notify-success:
extends:
- .only-canonical-schedules
- .notify
before_script:
- export COMMIT_NOTES_URL="https://$CI_SERVER_HOST/$CI_PROJECT_PATH/commit/$CI_COMMIT_SHA#notes-list"
variables:
COMMIT_NOTES_URL: "https://$CI_SERVER_HOST/$CI_PROJECT_PATH/commit/$CI_COMMIT_SHA#notes-list"
script:
- 'scripts/notify-slack qa-master ":tada: Scheduled QA against master passed! :tada: See $CI_PIPELINE_URL. For downstream pipelines, see $COMMIT_NOTES_URL" ci_passing'
needs: ["schedule:package-and-qa"]
Loading
Loading
@@ -21,8 +21,8 @@ schedule:package-and-qa:notify-failure:
extends:
- .only-canonical-schedules
- .notify
before_script:
- export COMMIT_NOTES_URL="https://$CI_SERVER_HOST/$CI_PROJECT_PATH/commit/$CI_COMMIT_SHA#notes-list"
variables:
COMMIT_NOTES_URL: "https://$CI_SERVER_HOST/$CI_PROJECT_PATH/commit/$CI_COMMIT_SHA#notes-list"
script:
- 'scripts/notify-slack qa-master ":skull_and_crossbones: Scheduled QA against master failed! :skull_and_crossbones: See $CI_PIPELINE_URL. For downstream pipelines, see $COMMIT_NOTES_URL" ci_failing'
needs: ["schedule:package-and-qa"]
Loading
Loading
Loading
Loading
@@ -27,11 +27,16 @@ export default class TemplateSelector {
search: {
fields: ['name'],
},
clicked: options => this.fetchFileTemplate(options),
clicked: options => this.onDropdownClicked(options),
text: item => item.name,
});
}
 
// Subclasses can override this method to conditionally prevent fetching file templates
onDropdownClicked(options) {
this.fetchFileTemplate(options);
}
initAutosizeUpdateEvent() {
this.autosizeUpdateEvent = document.createEvent('Event');
this.autosizeUpdateEvent.initEvent('autosize:update', true, false);
Loading
Loading
@@ -81,6 +86,10 @@ export default class TemplateSelector {
}
}
 
getEditorContent() {
return this.editor.getValue();
}
startLoadingSpinner() {
this.$dropdownIcon.addClass('fa-spinner fa-spin').removeClass('fa-chevron-down');
}
Loading
Loading
Loading
Loading
@@ -717,6 +717,7 @@ GitLabDropdown = (function() {
selectedObject = this.renderedData[groupName][selectedIndex];
} else {
selectedIndex = el.closest('li').index();
this.selectedIndex = selectedIndex;
selectedObject = this.renderedData[selectedIndex];
}
}
Loading
Loading
Loading
Loading
@@ -15,7 +15,9 @@ export default () => {
new IssuableForm($('.issue-form'));
new LabelsSelect();
new MilestoneSelect();
new IssuableTemplateSelectors();
new IssuableTemplateSelectors({
warnTemplateOverride: true,
});
 
initSuggestions();
};
Loading
Loading
@@ -16,5 +16,7 @@ export default () => {
new IssuableForm($('.merge-request-form'));
new LabelsSelect();
new MilestoneSelect();
new IssuableTemplateSelectors();
new IssuableTemplateSelectors({
warnTemplateOverride: true,
});
};
Loading
Loading
@@ -6,6 +6,9 @@ import Icon from '~/vue_shared/components/icon.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { __, n__, sprintf } from '../../locale';
import { slugify } from '~/lib/utils/text_utility';
import { getLocationHash } from '~/lib/utils/url_utility';
import { scrollToElement } from '~/lib/utils/common_utils';
 
export default {
name: 'ReleaseBlock',
Loading
Loading
@@ -26,7 +29,15 @@ export default {
default: () => ({}),
},
},
data() {
return {
isHighlighted: false,
};
},
computed: {
id() {
return slugify(this.release.tag_name);
},
releasedTimeAgo() {
return sprintf(__('released %{time}'), {
time: this.timeFormated(this.release.released_at),
Loading
Loading
@@ -62,10 +73,21 @@ export default {
return n__('Milestone', 'Milestones', this.release.milestones.length);
},
},
mounted() {
const hash = getLocationHash();
if (hash && slugify(hash) === this.id) {
this.isHighlighted = true;
setTimeout(() => {
this.isHighlighted = false;
}, 2000);
scrollToElement(this.$el);
}
},
};
</script>
<template>
<div :id="release.tag_name" class="card">
<div :id="id" :class="{ 'bg-line-target-blue': isHighlighted }" class="card release-block">
<div class="card-body">
<h2 class="card-title mt-0">
{{ release.name }}
Loading
Loading
Loading
Loading
@@ -8,10 +8,13 @@ import { __ } from '~/locale';
export default class IssuableTemplateSelector extends TemplateSelector {
constructor(...args) {
super(...args);
this.projectPath = this.dropdown.data('projectPath');
this.namespacePath = this.dropdown.data('namespacePath');
this.issuableType = this.$dropdownContainer.data('issuableType');
this.titleInput = $(`#${this.issuableType}_title`);
this.templateWarningEl = $('.js-issuable-template-warning');
this.warnTemplateOverride = args[0].warnTemplateOverride;
 
const initialQuery = {
name: this.dropdown.data('selected'),
Loading
Loading
@@ -24,14 +27,61 @@ export default class IssuableTemplateSelector extends TemplateSelector {
});
 
$('.no-template', this.dropdown.parent()).on('click', () => {
this.currentTemplate.content = '';
this.setInputValueToTemplateContent();
$('.dropdown-toggle-text', this.dropdown).text(__('Choose a template'));
this.reset();
});
this.templateWarningEl.find('.js-close-btn').on('click', () => {
if (this.previousSelectedIndex) {
this.dropdown.data('glDropdown').selectRowAtIndex(this.previousSelectedIndex);
} else {
this.reset();
}
this.templateWarningEl.addClass('hidden');
});
this.templateWarningEl.find('.js-override-template').on('click', () => {
this.requestFile(this.overridingTemplate);
this.setSelectedIndex();
this.templateWarningEl.addClass('hidden');
this.overridingTemplate = null;
});
}
 
reset() {
if (this.currentTemplate) {
this.currentTemplate.content = '';
}
this.setInputValueToTemplateContent();
$('.dropdown-toggle-text', this.dropdown).text(__('Choose a template'));
this.previousSelectedIndex = null;
}
setSelectedIndex() {
this.previousSelectedIndex = this.dropdown.data('glDropdown').selectedIndex;
}
onDropdownClicked(query) {
const content = this.getEditorContent();
const isContentUnchanged =
content === '' || (this.currentTemplate && content === this.currentTemplate.content);
if (!this.warnTemplateOverride || isContentUnchanged) {
super.onDropdownClicked(query);
this.setSelectedIndex();
return;
}
this.overridingTemplate = query.selectedObj;
this.templateWarningEl.removeClass('hidden');
}
requestFile(query) {
this.startLoadingSpinner();
Api.issueTemplate(
this.namespacePath,
this.projectPath,
Loading
Loading
Loading
Loading
@@ -4,7 +4,7 @@ import $ from 'jquery';
import IssuableTemplateSelector from './issuable_template_selector';
 
export default class IssuableTemplateSelectors {
constructor({ $dropdowns, editor } = {}) {
constructor({ $dropdowns, editor, warnTemplateOverride } = {}) {
this.$dropdowns = $dropdowns || $('.js-issuable-selector');
this.editor = editor || this.initEditor();
 
Loading
Loading
@@ -16,6 +16,7 @@ export default class IssuableTemplateSelectors {
wrapper: $dropdown.closest('.js-issuable-selector-wrap'),
dropdown: $dropdown,
editor: this.editor,
warnTemplateOverride,
});
});
}
Loading
Loading
Loading
Loading
@@ -211,7 +211,7 @@ export default {
<template v-else>
<review-app-link
:link="deploymentExternalUrl"
css-class="js-deploy-url js-deploy-url-feature-flag deploy-link btn btn-default btn-sm inline"
css-class="js-deploy-url deploy-link btn btn-default btn-sm inline"
/>
</template>
<visual-review-app-link
Loading
Loading
.release-block {
transition: background-color 1s linear;
}
Loading
Loading
@@ -55,6 +55,10 @@
background-color: $gray-light;
}
 
.bg-line-target-blue {
background: $line-target-blue;
}
.text-break-word {
word-break: break-all;
}
Loading
Loading
@@ -210,18 +214,26 @@ li.note {
@mixin message($background-color, $border-color, $text-color) {
border-left: 4px solid $border-color;
color: $text-color;
padding: 10px;
margin-bottom: 10px;
background: $background-color;
padding-left: 20px;
padding: $gl-padding $gl-padding-24;
margin-bottom: $gl-padding-12;
background-color: $background-color;
 
&.centered {
text-align: center;
}
.close {
svg {
width: $gl-font-size-large;
height: $gl-font-size-large;
}
color: inherit;
}
}
 
.warning_message {
@include message($orange-100, $orange-200, $orange-700);
@include message($orange-100, $orange-200, $orange-800);
}
 
.danger_message {
Loading
Loading
Loading
Loading
@@ -25,7 +25,7 @@ module Ci
belongs_to :merge_request, class_name: 'MergeRequest'
belongs_to :external_pull_request
 
has_internal_id :iid, scope: :project, presence: false, init: ->(s) do
has_internal_id :iid, scope: :project, presence: false, ensure_if: -> { !importing? }, init: ->(s) do
s&.project&.all_pipelines&.maximum(:iid) || s&.project&.all_pipelines&.count
end
 
Loading
Loading
Loading
Loading
@@ -27,53 +27,73 @@ module AtomicInternalId
extend ActiveSupport::Concern
 
class_methods do
def has_internal_id(column, scope:, init:, presence: true) # rubocop:disable Naming/PredicateName
def has_internal_id(column, scope:, init:, ensure_if: nil, presence: true) # rubocop:disable Naming/PredicateName
# We require init here to retain the ability to recalculate in the absence of a
# InternaLId record (we may delete records in `internal_ids` for example).
raise "has_internal_id requires a init block, none given." unless init
raise "has_internal_id needs to be defined on association." unless self.reflect_on_association(scope)
 
before_validation :"ensure_#{scope}_#{column}!", on: :create
before_validation :"track_#{scope}_#{column}!", on: :create
before_validation :"ensure_#{scope}_#{column}!", on: :create, if: ensure_if
validates column, presence: presence
 
define_method("ensure_#{scope}_#{column}!") do
scope_value = association(scope).reader
scope_value = internal_id_read_scope(scope)
value = read_attribute(column)
return value unless scope_value
 
scope_attrs = { scope_value.class.table_name.singularize.to_sym => scope_value }
usage = self.class.table_name.to_sym
if value.present? && (@iid_needs_tracking || Feature.enabled?(:iid_always_track, default_enabled: true))
# The value was set externally, e.g. by the user
# We update the InternalId record to keep track of the greatest value.
InternalId.track_greatest(self, scope_attrs, usage, value, init)
@iid_needs_tracking = false
elsif !value.present?
if value.nil?
# We don't have a value yet and use a InternalId record to generate
# the next value.
value = InternalId.generate_next(self, scope_attrs, usage, init)
value = InternalId.generate_next(
self,
internal_id_scope_attrs(scope),
internal_id_scope_usage,
init)
write_attribute(column, value)
end
 
value
end
 
define_method("track_#{scope}_#{column}!") do
iid_always_track = Feature.enabled?(:iid_always_track, default_enabled: true)
return unless @internal_id_needs_tracking || iid_always_track
@internal_id_needs_tracking = false
scope_value = internal_id_read_scope(scope)
value = read_attribute(column)
return unless scope_value
if value.present?
# The value was set externally, e.g. by the user
# We update the InternalId record to keep track of the greatest value.
InternalId.track_greatest(
self,
internal_id_scope_attrs(scope),
internal_id_scope_usage,
value,
init)
end
end
define_method("#{column}=") do |value|
super(value).tap do |v|
# Indicate the iid was set from externally
@iid_needs_tracking = true
@internal_id_needs_tracking = true
end
end
 
define_method("reset_#{scope}_#{column}") do
if value = read_attribute(column)
scope_value = association(scope).reader
scope_attrs = { scope_value.class.table_name.singularize.to_sym => scope_value }
usage = self.class.table_name.to_sym
did_reset = InternalId.reset(
self,
internal_id_scope_attrs(scope),
internal_id_scope_usage,
value)
 
if InternalId.reset(self, scope_attrs, usage, value)
if did_reset
write_attribute(column, nil)
end
end
Loading
Loading
@@ -82,4 +102,18 @@ module AtomicInternalId
end
end
end
def internal_id_scope_attrs(scope)
scope_value = internal_id_read_scope(scope)
{ scope_value.class.table_name.singularize.to_sym => scope_value } if scope_value
end
def internal_id_scope_usage
self.class.table_name.to_sym
end
def internal_id_read_scope(scope)
association(scope).reader
end
end
.form-group.row.js-template-warning.mb-0.hidden.js-issuable-template-warning
.offset-sm-2.col-sm-10
.warning_message.mb-0{ role: 'alert' }
%btn.js-close-btn.close{ type: "button", "aria-hidden": true, "aria-label": _("Close") }
= sprite_icon("close")
%p
= _("Applying a template will replace the existing issue description. Any changes you have made will be lost.")
%button.js-override-template.btn.btn-warning.mr-2{ type: 'button' }
= _("Apply template")
%button.js-cancel-btn.btn.btn-inverted{ type: 'button' }
= _("Cancel")
Loading
Loading
@@ -19,6 +19,7 @@
= render 'shared/issuable/form/title', issuable: issuable, form: form, has_wip_commits: commits && commits.detect(&:work_in_progress?)
#js-suggestions{ data: { project_path: @project.full_path } }
 
= render 'shared/form_elements/apply_template_warning'
= render 'shared/form_elements/description', model: issuable, form: form, project: project
 
- if issuable.respond_to?(:confidential)
Loading
Loading
---
title: Warn before applying issue templates
merge_request: 16865
author:
type: changed
---
title: Allow releases to be targeted by URL anchor links on the Releases page
merge_request: 17150
author:
type: added
# frozen_string_literal: true
class AddIndexPackagesOnNameTrigramToPackagesPackages < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_packages_packages_on_name_trigram'
disable_ddl_transaction!
def up
add_concurrent_index :packages_packages, :name, name: INDEX_NAME, using: :gin, opclass: { name: :gin_trgm_ops }
end
def down
remove_concurrent_index_by_name(:packages_packages, INDEX_NAME)
end
end
Loading
Loading
@@ -2556,6 +2556,7 @@ ActiveRecord::Schema.define(version: 2019_09_27_074328) do
t.string "name", null: false
t.string "version"
t.integer "package_type", limit: 2, null: false
t.index ["name"], name: "index_packages_packages_on_name_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["project_id"], name: "index_packages_packages_on_project_id"
end
 
Loading
Loading
Loading
Loading
@@ -48,10 +48,9 @@ as appropriate.
## Set a global Git hook for all repositories
 
To create a Git hook that applies to all of your repositories in
your instance, set a global Git hook. Since all the repositories' `hooks`
directories are symlinked to GitLab Shell's `hooks` directory, adding any hook
to the GitLab Shell `hooks` directory will also apply it to all repositories. Follow
the steps below to properly set up a custom hook for all repositories:
your instance, set a global Git hook. Since GitLab will look inside the GitLab Shell
`hooks` directory for global hooks, adding any hook there will apply it to all repositories.
Follow the steps below to properly set up a custom hook for all repositories:
 
1. On the GitLab server, navigate to the configured custom hook directory. The
default is in the GitLab Shell directory. The GitLab Shell `hook` directory
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment