Merge branch '35616-move-k8-to-cluster-page' into cluster-page-with-list-clusters
This commit is contained in:
commit
9429514e30
|
@ -1 +1 @@
|
|||
0.55.0
|
||||
0.56.0
|
||||
|
|
|
@ -1 +1 @@
|
|||
5.9.4
|
||||
5.10.0
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -1,6 +1,6 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rails', '4.2.8'
|
||||
gem 'rails', '4.2.10'
|
||||
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
|
||||
|
||||
# Responders respond_to and respond_with
|
||||
|
@ -400,7 +400,7 @@ group :ed25519 do
|
|||
end
|
||||
|
||||
# Gitaly GRPC client
|
||||
gem 'gitaly-proto', '~> 0.54.0', require: 'gitaly'
|
||||
gem 'gitaly-proto', '~> 0.58.0', require: 'gitaly'
|
||||
|
||||
gem 'toml-rb', '~> 0.3.15', require: false
|
||||
|
||||
|
|
85
Gemfile.lock
85
Gemfile.lock
|
@ -4,38 +4,38 @@ GEM
|
|||
RedCloth (4.3.2)
|
||||
abstract_type (0.0.7)
|
||||
ace-rails-ap (4.1.2)
|
||||
actionmailer (4.2.8)
|
||||
actionpack (= 4.2.8)
|
||||
actionview (= 4.2.8)
|
||||
activejob (= 4.2.8)
|
||||
actionmailer (4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activejob (= 4.2.10)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
actionpack (4.2.8)
|
||||
actionview (= 4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
actionpack (4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
rack (~> 1.6)
|
||||
rack-test (~> 0.6.2)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
actionview (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
activejob (4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
activejob (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
globalid (>= 0.3.0)
|
||||
activemodel (4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
activemodel (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.8)
|
||||
activemodel (= 4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
activerecord (4.2.10)
|
||||
activemodel (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
arel (~> 6.0)
|
||||
activerecord_sane_schema_dumper (0.2)
|
||||
rails (>= 4, < 5)
|
||||
activesupport (4.2.8)
|
||||
activesupport (4.2.10)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
|
@ -276,7 +276,7 @@ GEM
|
|||
po_to_json (>= 1.0.0)
|
||||
rails (>= 3.2.0)
|
||||
gherkin-ruby (0.3.2)
|
||||
gitaly-proto (0.54.0)
|
||||
gitaly-proto (0.58.0)
|
||||
google-protobuf (~> 3.1)
|
||||
grpc (~> 1.0)
|
||||
github-linguist (4.7.6)
|
||||
|
@ -300,8 +300,8 @@ GEM
|
|||
omniauth (~> 1.3)
|
||||
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
|
||||
rubyntlm (~> 0.5)
|
||||
globalid (0.3.7)
|
||||
activesupport (>= 4.1.0)
|
||||
globalid (0.4.1)
|
||||
activesupport (>= 4.2.0)
|
||||
gollum-grit_adapter (1.0.1)
|
||||
gitlab-grit (~> 2.7, >= 2.7.1)
|
||||
gollum-lib (4.2.7)
|
||||
|
@ -400,7 +400,8 @@ GEM
|
|||
json (~> 1.8)
|
||||
multi_xml (>= 0.5.2)
|
||||
httpclient (2.8.2)
|
||||
i18n (0.8.6)
|
||||
i18n (0.9.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
ice_nine (0.11.2)
|
||||
influxdb (0.2.3)
|
||||
cause
|
||||
|
@ -474,8 +475,8 @@ GEM
|
|||
railties (>= 4, < 5.2)
|
||||
loofah (2.0.3)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.6.6)
|
||||
mime-types (>= 1.16, < 4)
|
||||
mail (2.7.0)
|
||||
mini_mime (>= 0.1.1)
|
||||
mail_room (0.9.1)
|
||||
memoist (0.16.0)
|
||||
memoizable (0.4.2)
|
||||
|
@ -573,8 +574,8 @@ GEM
|
|||
parallel (1.12.0)
|
||||
paranoia (2.3.1)
|
||||
activerecord (>= 4.0, < 5.2)
|
||||
parser (2.4.0.0)
|
||||
ast (~> 2.2)
|
||||
parser (2.4.0.2)
|
||||
ast (~> 2.3)
|
||||
parslet (1.5.0)
|
||||
blankslate (~> 2.0)
|
||||
path_expander (1.0.1)
|
||||
|
@ -656,16 +657,16 @@ GEM
|
|||
rack
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (4.2.8)
|
||||
actionmailer (= 4.2.8)
|
||||
actionpack (= 4.2.8)
|
||||
actionview (= 4.2.8)
|
||||
activejob (= 4.2.8)
|
||||
activemodel (= 4.2.8)
|
||||
activerecord (= 4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
rails (4.2.10)
|
||||
actionmailer (= 4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activejob (= 4.2.10)
|
||||
activemodel (= 4.2.10)
|
||||
activerecord (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.2.8)
|
||||
railties (= 4.2.10)
|
||||
sprockets-rails
|
||||
rails-deprecated_sanitizer (1.0.3)
|
||||
activesupport (>= 4.2.0.alpha)
|
||||
|
@ -678,15 +679,15 @@ GEM
|
|||
rails-i18n (4.0.9)
|
||||
i18n (~> 0.7)
|
||||
railties (~> 4.0)
|
||||
railties (4.2.8)
|
||||
actionpack (= 4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
railties (4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (2.2.2)
|
||||
rake
|
||||
raindrops (0.18.0)
|
||||
rake (12.1.0)
|
||||
rake (12.3.0)
|
||||
rblineprof (0.3.6)
|
||||
debugger-ruby_core_source (~> 1.3)
|
||||
rbnacl (4.0.2)
|
||||
|
@ -873,7 +874,7 @@ GEM
|
|||
sprockets (3.7.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (3.2.0)
|
||||
sprockets-rails (3.2.1)
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
|
@ -911,7 +912,7 @@ GEM
|
|||
truncato (0.7.10)
|
||||
htmlentities (~> 4.3.1)
|
||||
nokogiri (~> 1.8.0, >= 1.7.0)
|
||||
tzinfo (1.2.3)
|
||||
tzinfo (1.2.4)
|
||||
thread_safe (~> 0.1)
|
||||
u2f (0.2.1)
|
||||
uber (0.1.0)
|
||||
|
@ -1036,7 +1037,7 @@ DEPENDENCIES
|
|||
gettext (~> 3.2.2)
|
||||
gettext_i18n_rails (~> 1.8.0)
|
||||
gettext_i18n_rails_js (~> 1.2.0)
|
||||
gitaly-proto (~> 0.54.0)
|
||||
gitaly-proto (~> 0.58.0)
|
||||
github-linguist (~> 4.7.0)
|
||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||
gitlab-markup (~> 1.6.2)
|
||||
|
@ -1118,7 +1119,7 @@ DEPENDENCIES
|
|||
rack-cors (~> 0.4.0)
|
||||
rack-oauth2 (~> 1.2.1)
|
||||
rack-proxy (~> 0.6.0)
|
||||
rails (= 4.2.8)
|
||||
rails (= 4.2.10)
|
||||
rails-deprecated_sanitizer (~> 1.0.3)
|
||||
rails-i18n (~> 4.0.9)
|
||||
rainbow (~> 2.2)
|
||||
|
|
|
@ -20,6 +20,7 @@ class ListIssue {
|
|||
this.isFetching = {
|
||||
subscriptions: true,
|
||||
};
|
||||
this.isLoading = {};
|
||||
this.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
|
||||
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
|
||||
|
||||
|
@ -86,6 +87,10 @@ class ListIssue {
|
|||
this.isFetching[key] = value;
|
||||
}
|
||||
|
||||
setLoadingState(key, value) {
|
||||
this.isLoading[key] = value;
|
||||
}
|
||||
|
||||
update (url) {
|
||||
const data = {
|
||||
issue: {
|
||||
|
|
|
@ -3,3 +3,4 @@ import './polyfills';
|
|||
import './jquery';
|
||||
import './bootstrap';
|
||||
import './vue';
|
||||
import '../lib/utils/axios_utils';
|
||||
|
|
|
@ -514,10 +514,11 @@ GitLabDropdown = (function() {
|
|||
|
||||
const dropdownToggle = this.dropdown.find('.dropdown-menu-toggle');
|
||||
const hasFilterBulkUpdate = dropdownToggle.hasClass('js-filter-bulk-update');
|
||||
const shouldRefreshOnOpen = dropdownToggle.hasClass('js-gl-dropdown-refresh-on-open');
|
||||
const hasMultiSelect = dropdownToggle.hasClass('js-multiselect');
|
||||
|
||||
// Makes indeterminate items effective
|
||||
if (this.fullData && hasFilterBulkUpdate) {
|
||||
if (this.fullData && (shouldRefreshOnOpen || hasFilterBulkUpdate)) {
|
||||
this.parseData(this.fullData);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
<script>
|
||||
import Icon from '../../vue_shared/components/icon.vue';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import Issuable from '~/vue_shared/mixins/issuable';
|
||||
|
||||
export default {
|
||||
component: {
|
||||
mixins: [
|
||||
Issuable,
|
||||
],
|
||||
components: {
|
||||
Icon,
|
||||
},
|
||||
};
|
||||
|
@ -16,7 +20,7 @@
|
|||
:size="16"
|
||||
class="icon">
|
||||
</icon>
|
||||
<span>This issue is locked. Only <b>project members</b> can comment.</span>
|
||||
<span>This {{ issuableDisplayName }} is locked. Only <b>project members</b> can comment.</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
|
@ -8,8 +8,8 @@
|
|||
import * as constants from '../constants';
|
||||
import eventHub from '../event_hub';
|
||||
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
|
||||
import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue';
|
||||
import issueDiscussionLockedWidget from './issue_discussion_locked_widget.vue';
|
||||
import noteSignedOutWidget from './note_signed_out_widget.vue';
|
||||
import discussionLockedWidget from './discussion_locked_widget.vue';
|
||||
import markdownField from '../../vue_shared/components/markdown/field.vue';
|
||||
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
import issuableStateMixin from '../mixins/issuable_state';
|
||||
|
@ -29,8 +29,8 @@
|
|||
},
|
||||
components: {
|
||||
issueWarning,
|
||||
issueNoteSignedOutWidget,
|
||||
issueDiscussionLockedWidget,
|
||||
noteSignedOutWidget,
|
||||
discussionLockedWidget,
|
||||
markdownField,
|
||||
userAvatarLink,
|
||||
},
|
||||
|
@ -240,8 +240,11 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<issue-note-signed-out-widget v-if="!isLoggedIn" />
|
||||
<issue-discussion-locked-widget v-else-if="!canCreateNote" />
|
||||
<note-signed-out-widget v-if="!isLoggedIn" />
|
||||
<discussion-locked-widget
|
||||
issuable-type="issue"
|
||||
v-else-if="!canCreateNote"
|
||||
/>
|
||||
<ul
|
||||
v-else
|
||||
class="notes notes-form timeline">
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
import { SYSTEM_NOTE } from '../constants';
|
||||
import issueNote from './issue_note.vue';
|
||||
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
import issueNoteHeader from './issue_note_header.vue';
|
||||
import issueNoteActions from './issue_note_actions.vue';
|
||||
import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue';
|
||||
import issueNoteEditedText from './issue_note_edited_text.vue';
|
||||
import noteHeader from './note_header.vue';
|
||||
import noteSignedOutWidget from './note_signed_out_widget.vue';
|
||||
import noteEditedText from './note_edited_text.vue';
|
||||
import issueNoteForm from './issue_note_form.vue';
|
||||
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
|
||||
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
|
||||
|
@ -28,10 +27,9 @@
|
|||
components: {
|
||||
issueNote,
|
||||
userAvatarLink,
|
||||
issueNoteHeader,
|
||||
issueNoteActions,
|
||||
issueNoteSignedOutWidget,
|
||||
issueNoteEditedText,
|
||||
noteHeader,
|
||||
noteSignedOutWidget,
|
||||
noteEditedText,
|
||||
issueNoteForm,
|
||||
placeholderNote,
|
||||
placeholderSystemNote,
|
||||
|
@ -171,7 +169,7 @@
|
|||
<div class="timeline-content">
|
||||
<div class="discussion">
|
||||
<div class="discussion-header">
|
||||
<issue-note-header
|
||||
<note-header
|
||||
:author="author"
|
||||
:created-at="discussion.created_at"
|
||||
:note-id="discussion.id"
|
||||
|
@ -179,8 +177,8 @@
|
|||
@toggleHandler="toggleDiscussionHandler"
|
||||
action-text="started a discussion"
|
||||
class="discussion"
|
||||
/>
|
||||
<issue-note-edited-text
|
||||
/>
|
||||
<note-edited-text
|
||||
v-if="lastUpdatedAt"
|
||||
:edited-at="lastUpdatedAt"
|
||||
:edited-by="lastUpdatedBy"
|
||||
|
@ -220,7 +218,7 @@
|
|||
@cancelFormEdition="cancelReplyForm"
|
||||
ref="noteForm"
|
||||
/>
|
||||
<issue-note-signed-out-widget v-if="!canReply" />
|
||||
<note-signed-out-widget v-if="!canReply" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
import { mapGetters, mapActions } from 'vuex';
|
||||
import Flash from '../../flash';
|
||||
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
import issueNoteHeader from './issue_note_header.vue';
|
||||
import issueNoteActions from './issue_note_actions.vue';
|
||||
import noteHeader from './note_header.vue';
|
||||
import noteActions from './note_actions.vue';
|
||||
import issueNoteBody from './issue_note_body.vue';
|
||||
import eventHub from '../event_hub';
|
||||
|
||||
|
@ -23,8 +23,8 @@
|
|||
},
|
||||
components: {
|
||||
userAvatarLink,
|
||||
issueNoteHeader,
|
||||
issueNoteActions,
|
||||
noteHeader,
|
||||
noteActions,
|
||||
issueNoteBody,
|
||||
},
|
||||
computed: {
|
||||
|
@ -155,13 +155,13 @@
|
|||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="note-header">
|
||||
<issue-note-header
|
||||
<note-header
|
||||
:author="author"
|
||||
:created-at="note.created_at"
|
||||
:note-id="note.id"
|
||||
action-text="commented"
|
||||
/>
|
||||
<issue-note-actions
|
||||
<note-actions
|
||||
:author-id="author.id"
|
||||
:note-id="note.id"
|
||||
:access-level="note.human_access"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import issueNoteEditedText from './issue_note_edited_text.vue';
|
||||
import issueNoteAwardsList from './issue_note_awards_list.vue';
|
||||
import issueNoteAttachment from './issue_note_attachment.vue';
|
||||
import noteEditedText from './note_edited_text.vue';
|
||||
import noteAwardsList from './note_awards_list.vue';
|
||||
import noteAttachment from './note_attachment.vue';
|
||||
import issueNoteForm from './issue_note_form.vue';
|
||||
import TaskList from '../../task_list';
|
||||
import autosave from '../mixins/autosave';
|
||||
|
@ -26,9 +26,9 @@
|
|||
autosave,
|
||||
],
|
||||
components: {
|
||||
issueNoteEditedText,
|
||||
issueNoteAwardsList,
|
||||
issueNoteAttachment,
|
||||
noteEditedText,
|
||||
noteAwardsList,
|
||||
noteAttachment,
|
||||
issueNoteForm,
|
||||
},
|
||||
computed: {
|
||||
|
@ -101,20 +101,20 @@
|
|||
v-model="note.note"
|
||||
:data-update-url="note.path"
|
||||
class="hidden js-task-list-field"></textarea>
|
||||
<issue-note-edited-text
|
||||
<note-edited-text
|
||||
v-if="note.last_edited_at"
|
||||
:edited-at="note.last_edited_at"
|
||||
:edited-by="note.last_edited_by"
|
||||
action-text="Edited"
|
||||
/>
|
||||
<issue-note-awards-list
|
||||
<note-awards-list
|
||||
v-if="note.award_emoji.length"
|
||||
:note-id="note.id"
|
||||
:note-author-id="note.author.id"
|
||||
:awards="note.award_emoji"
|
||||
:toggle-award-path="note.toggle_award_path"
|
||||
/>
|
||||
<issue-note-attachment
|
||||
<note-attachment
|
||||
v-if="note.attachment"
|
||||
:attachment="note.attachment"
|
||||
/>
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
import emojiSmiley from 'icons/_emoji_smiley.svg';
|
||||
import editSvg from 'icons/_icon_pencil.svg';
|
||||
import ellipsisSvg from 'icons/_ellipsis_v.svg';
|
||||
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
||||
import tooltip from '../../vue_shared/directives/tooltip';
|
||||
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
|
||||
import tooltip from '~/vue_shared/directives/tooltip';
|
||||
|
||||
export default {
|
||||
name: 'issueNoteActions',
|
||||
name: 'noteActions',
|
||||
props: {
|
||||
authorId: {
|
||||
type: Number,
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'issueNoteAttachment',
|
||||
name: 'noteAttachment',
|
||||
props: {
|
||||
attachment: {
|
||||
type: Object,
|
|
@ -2,7 +2,6 @@
|
|||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'singInLinksNotes',
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'getNotesDataByProp',
|
|
@ -78,11 +78,13 @@
|
|||
<div class="ci-job-component">
|
||||
<a
|
||||
v-tooltip
|
||||
v-if="job.status.details_path"
|
||||
v-if="job.status.has_details"
|
||||
:href="job.status.details_path"
|
||||
:title="tooltipText"
|
||||
:class="cssClassJobName"
|
||||
data-container="body">
|
||||
data-container="body"
|
||||
class="js-pipeline-graph-job-link"
|
||||
>
|
||||
|
||||
<job-name-component
|
||||
:name="job.name"
|
||||
|
@ -95,7 +97,8 @@
|
|||
v-tooltip
|
||||
:title="tooltipText"
|
||||
:class="cssClassJobName"
|
||||
data-container="body">
|
||||
data-container="body"
|
||||
>
|
||||
|
||||
<job-name-component
|
||||
:name="job.name"
|
||||
|
|
|
@ -15,7 +15,7 @@ import Cookies from 'js-cookie';
|
|||
|
||||
Sidebar.prototype.removeListeners = function () {
|
||||
this.sidebar.off('click', '.sidebar-collapsed-icon');
|
||||
$('.dropdown').off('hidden.gl.dropdown');
|
||||
this.sidebar.off('hidden.gl.dropdown');
|
||||
$('.dropdown').off('loading.gl.dropdown');
|
||||
$('.dropdown').off('loaded.gl.dropdown');
|
||||
$(document).off('click', '.js-sidebar-toggle');
|
||||
|
@ -25,7 +25,7 @@ import Cookies from 'js-cookie';
|
|||
const $document = $(document);
|
||||
|
||||
this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked);
|
||||
$('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden);
|
||||
this.sidebar.on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden);
|
||||
$('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading);
|
||||
$('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
|
||||
|
||||
|
@ -180,7 +180,7 @@ import Cookies from 'js-cookie';
|
|||
var $block, sidebar;
|
||||
sidebar = e.data;
|
||||
e.preventDefault();
|
||||
$block = $(this).closest('.block');
|
||||
$block = $(e.target).closest('.block');
|
||||
return sidebar.sidebarDropdownHidden($block);
|
||||
};
|
||||
|
||||
|
|
|
@ -18,11 +18,6 @@ export default {
|
|||
required: true,
|
||||
type: Function,
|
||||
},
|
||||
|
||||
issuableType: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
|
||||
mixins: [
|
||||
|
@ -39,13 +34,13 @@ export default {
|
|||
<div class="dropdown open">
|
||||
<div class="dropdown-menu sidebar-item-warning-message">
|
||||
<p class="text" v-if="isLocked">
|
||||
Unlock this {{ issuableDisplayName(issuableType) }}?
|
||||
Unlock this {{ issuableDisplayName }}?
|
||||
<strong>Everyone</strong>
|
||||
will be able to comment.
|
||||
</p>
|
||||
|
||||
<p class="text" v-else>
|
||||
Lock this {{ issuableDisplayName(issuableType) }}?
|
||||
Lock this {{ issuableDisplayName }}?
|
||||
Only
|
||||
<strong>project members</strong>
|
||||
will be able to comment.
|
||||
|
|
|
@ -23,11 +23,6 @@ export default {
|
|||
return mediatorObject.service && mediatorObject.service.update && mediatorObject.store;
|
||||
},
|
||||
},
|
||||
|
||||
issuableType: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
|
||||
mixins: [
|
||||
|
@ -59,7 +54,7 @@ export default {
|
|||
discussion_locked: locked,
|
||||
})
|
||||
.then(() => location.reload())
|
||||
.catch(() => Flash(this.__(`Something went wrong trying to change the locked state of this ${this.issuableDisplayName(this.issuableType)}`)));
|
||||
.catch(() => Flash(this.__(`Something went wrong trying to change the locked state of this ${this.issuableDisplayName}`)));
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -77,7 +72,7 @@ export default {
|
|||
</div>
|
||||
|
||||
<div class="title hide-collapsed">
|
||||
Lock {{issuableDisplayName(issuableType) }}
|
||||
Lock {{ issuableDisplayName }}
|
||||
<button
|
||||
v-if="isEditable"
|
||||
class="pull-right lock-edit btn btn-blank"
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
import Vue from 'vue';
|
||||
import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking';
|
||||
import SidebarAssignees from './components/assignees/sidebar_assignees';
|
||||
import ConfidentialIssueSidebar from './components/confidential/confidential_issue_sidebar.vue';
|
||||
import SidebarMoveIssue from './lib/sidebar_move_issue';
|
||||
import LockIssueSidebar from './components/lock/lock_issue_sidebar.vue';
|
||||
import sidebarParticipants from './components/participants/sidebar_participants.vue';
|
||||
import sidebarSubscriptions from './components/subscriptions/sidebar_subscriptions.vue';
|
||||
import Translate from '../vue_shared/translate';
|
||||
|
||||
Vue.use(Translate);
|
||||
|
||||
function mountConfidentialComponent(mediator) {
|
||||
const el = document.getElementById('js-confidential-entry-point');
|
||||
|
||||
if (!el) return;
|
||||
|
||||
const dataNode = document.getElementById('js-confidential-issue-data');
|
||||
const initialData = JSON.parse(dataNode.innerHTML);
|
||||
|
||||
const ConfidentialComp = Vue.extend(ConfidentialIssueSidebar);
|
||||
|
||||
new ConfidentialComp({
|
||||
propsData: {
|
||||
isConfidential: initialData.is_confidential,
|
||||
isEditable: initialData.is_editable,
|
||||
service: mediator.service,
|
||||
},
|
||||
}).$mount(el);
|
||||
}
|
||||
|
||||
function mountLockComponent(mediator) {
|
||||
const el = document.getElementById('js-lock-entry-point');
|
||||
|
||||
if (!el) return;
|
||||
|
||||
const dataNode = document.getElementById('js-lock-issue-data');
|
||||
const initialData = JSON.parse(dataNode.innerHTML);
|
||||
|
||||
const LockComp = Vue.extend(LockIssueSidebar);
|
||||
|
||||
new LockComp({
|
||||
propsData: {
|
||||
isLocked: initialData.is_locked,
|
||||
isEditable: initialData.is_editable,
|
||||
mediator,
|
||||
issuableType: gl.utils.isInIssuePage() ? 'issue' : 'merge_request',
|
||||
},
|
||||
}).$mount(el);
|
||||
}
|
||||
|
||||
function mountParticipantsComponent() {
|
||||
const el = document.querySelector('.js-sidebar-participants-entry-point');
|
||||
|
||||
if (!el) return;
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
components: {
|
||||
sidebarParticipants,
|
||||
},
|
||||
render: createElement => createElement('sidebar-participants', {}),
|
||||
});
|
||||
}
|
||||
|
||||
function mountSubscriptionsComponent() {
|
||||
const el = document.querySelector('.js-sidebar-subscriptions-entry-point');
|
||||
|
||||
if (!el) return;
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
components: {
|
||||
sidebarSubscriptions,
|
||||
},
|
||||
render: createElement => createElement('sidebar-subscriptions', {}),
|
||||
});
|
||||
}
|
||||
|
||||
function mount(mediator) {
|
||||
const sidebarAssigneesEl = document.getElementById('js-vue-sidebar-assignees');
|
||||
// Only create the sidebarAssignees vue app if it is found in the DOM
|
||||
// We currently do not use sidebarAssignees for the MR page
|
||||
if (sidebarAssigneesEl) {
|
||||
new Vue(SidebarAssignees).$mount(sidebarAssigneesEl);
|
||||
}
|
||||
|
||||
mountConfidentialComponent(mediator);
|
||||
mountLockComponent(mediator);
|
||||
mountParticipantsComponent();
|
||||
mountSubscriptionsComponent();
|
||||
|
||||
new SidebarMoveIssue(
|
||||
mediator,
|
||||
$('.js-move-issue'),
|
||||
$('.js-move-issue-confirmation-button'),
|
||||
).init();
|
||||
|
||||
new Vue(SidebarTimeTracking).$mount('#issuable-time-tracker');
|
||||
}
|
||||
|
||||
export default mount;
|
|
@ -1,110 +1,12 @@
|
|||
import Vue from 'vue';
|
||||
import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking';
|
||||
import SidebarAssignees from './components/assignees/sidebar_assignees';
|
||||
import ConfidentialIssueSidebar from './components/confidential/confidential_issue_sidebar.vue';
|
||||
import SidebarMoveIssue from './lib/sidebar_move_issue';
|
||||
import LockIssueSidebar from './components/lock/lock_issue_sidebar.vue';
|
||||
import sidebarParticipants from './components/participants/sidebar_participants.vue';
|
||||
import sidebarSubscriptions from './components/subscriptions/sidebar_subscriptions.vue';
|
||||
import Translate from '../vue_shared/translate';
|
||||
|
||||
import Mediator from './sidebar_mediator';
|
||||
|
||||
Vue.use(Translate);
|
||||
|
||||
function mountConfidentialComponent(mediator) {
|
||||
const el = document.getElementById('js-confidential-entry-point');
|
||||
|
||||
if (!el) return;
|
||||
|
||||
const dataNode = document.getElementById('js-confidential-issue-data');
|
||||
const initialData = JSON.parse(dataNode.innerHTML);
|
||||
|
||||
const ConfidentialComp = Vue.extend(ConfidentialIssueSidebar);
|
||||
|
||||
new ConfidentialComp({
|
||||
propsData: {
|
||||
isConfidential: initialData.is_confidential,
|
||||
isEditable: initialData.is_editable,
|
||||
service: mediator.service,
|
||||
},
|
||||
}).$mount(el);
|
||||
}
|
||||
|
||||
function mountLockComponent(mediator) {
|
||||
const el = document.getElementById('js-lock-entry-point');
|
||||
|
||||
if (!el) return;
|
||||
|
||||
const dataNode = document.getElementById('js-lock-issue-data');
|
||||
const initialData = JSON.parse(dataNode.innerHTML);
|
||||
|
||||
const LockComp = Vue.extend(LockIssueSidebar);
|
||||
|
||||
new LockComp({
|
||||
propsData: {
|
||||
isLocked: initialData.is_locked,
|
||||
isEditable: initialData.is_editable,
|
||||
mediator,
|
||||
issuableType: gl.utils.isInIssuePage() ? 'issue' : 'merge_request',
|
||||
},
|
||||
}).$mount(el);
|
||||
}
|
||||
|
||||
function mountParticipantsComponent() {
|
||||
const el = document.querySelector('.js-sidebar-participants-entry-point');
|
||||
|
||||
if (!el) return;
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
components: {
|
||||
sidebarParticipants,
|
||||
},
|
||||
render: createElement => createElement('sidebar-participants', {}),
|
||||
});
|
||||
}
|
||||
|
||||
function mountSubscriptionsComponent() {
|
||||
const el = document.querySelector('.js-sidebar-subscriptions-entry-point');
|
||||
|
||||
if (!el) return;
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
components: {
|
||||
sidebarSubscriptions,
|
||||
},
|
||||
render: createElement => createElement('sidebar-subscriptions', {}),
|
||||
});
|
||||
}
|
||||
import mountSidebar from './mount_sidebar';
|
||||
|
||||
function domContentLoaded() {
|
||||
const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML);
|
||||
const mediator = new Mediator(sidebarOptions);
|
||||
mediator.fetch();
|
||||
|
||||
const sidebarAssigneesEl = document.getElementById('js-vue-sidebar-assignees');
|
||||
// Only create the sidebarAssignees vue app if it is found in the DOM
|
||||
// We currently do not use sidebarAssignees for the MR page
|
||||
if (sidebarAssigneesEl) {
|
||||
new Vue(SidebarAssignees).$mount(sidebarAssigneesEl);
|
||||
}
|
||||
|
||||
mountConfidentialComponent(mediator);
|
||||
mountLockComponent(mediator);
|
||||
mountParticipantsComponent();
|
||||
mountSubscriptionsComponent();
|
||||
|
||||
new SidebarMoveIssue(
|
||||
mediator,
|
||||
$('.js-move-issue'),
|
||||
$('.js-move-issue-confirmation-button'),
|
||||
).init();
|
||||
|
||||
new Vue(SidebarTimeTracking).$mount('#issuable-time-tracker');
|
||||
mountSidebar(mediator);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', domContentLoaded);
|
||||
|
|
|
@ -5,19 +5,23 @@ import Store from './stores/sidebar_store';
|
|||
export default class SidebarMediator {
|
||||
constructor(options) {
|
||||
if (!SidebarMediator.singleton) {
|
||||
this.store = new Store(options);
|
||||
this.service = new Service({
|
||||
endpoint: options.endpoint,
|
||||
toggleSubscriptionEndpoint: options.toggleSubscriptionEndpoint,
|
||||
moveIssueEndpoint: options.moveIssueEndpoint,
|
||||
projectsAutocompleteEndpoint: options.projectsAutocompleteEndpoint,
|
||||
});
|
||||
SidebarMediator.singleton = this;
|
||||
this.initSingleton(options);
|
||||
}
|
||||
|
||||
return SidebarMediator.singleton;
|
||||
}
|
||||
|
||||
initSingleton(options) {
|
||||
this.store = new Store(options);
|
||||
this.service = new Service({
|
||||
endpoint: options.endpoint,
|
||||
toggleSubscriptionEndpoint: options.toggleSubscriptionEndpoint,
|
||||
moveIssueEndpoint: options.moveIssueEndpoint,
|
||||
projectsAutocompleteEndpoint: options.projectsAutocompleteEndpoint,
|
||||
});
|
||||
SidebarMediator.singleton = this;
|
||||
}
|
||||
|
||||
assignYourself() {
|
||||
this.store.addAssignee(this.store.currentUser);
|
||||
}
|
||||
|
@ -35,17 +39,21 @@ export default class SidebarMediator {
|
|||
}
|
||||
|
||||
fetch() {
|
||||
this.service.get()
|
||||
return this.service.get()
|
||||
.then(response => response.json())
|
||||
.then((data) => {
|
||||
this.store.setAssigneeData(data);
|
||||
this.store.setTimeTrackingData(data);
|
||||
this.store.setParticipantsData(data);
|
||||
this.store.setSubscriptionsData(data);
|
||||
this.processFetchedData(data);
|
||||
})
|
||||
.catch(() => new Flash('Error occurred when fetching sidebar data'));
|
||||
}
|
||||
|
||||
processFetchedData(data) {
|
||||
this.store.setAssigneeData(data);
|
||||
this.store.setTimeTrackingData(data);
|
||||
this.store.setParticipantsData(data);
|
||||
this.store.setSubscriptionsData(data);
|
||||
}
|
||||
|
||||
toggleSubscription() {
|
||||
this.store.setFetchingState('subscriptions', true);
|
||||
return this.service.toggleSubscription()
|
||||
|
|
|
@ -15,6 +15,7 @@ export default class SidebarStore {
|
|||
participants: true,
|
||||
subscriptions: true,
|
||||
};
|
||||
this.isLoading = {};
|
||||
this.autocompleteProjects = [];
|
||||
this.moveToProjectId = 0;
|
||||
this.isLockDialogOpen = false;
|
||||
|
@ -55,6 +56,10 @@ export default class SidebarStore {
|
|||
this.isFetching[key] = value;
|
||||
}
|
||||
|
||||
setLoadingState(key, value) {
|
||||
this.isLoading[key] = value;
|
||||
}
|
||||
|
||||
addAssignee(assignee) {
|
||||
if (!this.findAssignee(assignee)) {
|
||||
this.assignees.push(assignee);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* />
|
||||
*/
|
||||
import { mapGetters } from 'vuex';
|
||||
import issueNoteHeader from '../../../notes/components/issue_note_header.vue';
|
||||
import noteHeader from '~/notes/components/note_header.vue';
|
||||
import { spriteIcon } from '../../../lib/utils/common_utils';
|
||||
|
||||
export default {
|
||||
|
@ -29,7 +29,7 @@
|
|||
},
|
||||
},
|
||||
components: {
|
||||
issueNoteHeader,
|
||||
noteHeader,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
|
@ -60,12 +60,12 @@
|
|||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="note-header">
|
||||
<issue-note-header
|
||||
<note-header
|
||||
:author="note.author"
|
||||
:created-at="note.created_at"
|
||||
:note-id="note.id"
|
||||
:action-text-html="note.note_html"
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
export default {
|
||||
methods: {
|
||||
issuableDisplayName(issuableType) {
|
||||
const displayName = issuableType.replace(/_/, ' ');
|
||||
props: {
|
||||
issuableType: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
|
||||
return this.__ ? this.__(displayName) : displayName;
|
||||
computed: {
|
||||
issuableDisplayName() {
|
||||
return this.issuableType.replace(/_/g, ' ');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
color: $brand-info;
|
||||
}
|
||||
|
||||
.underlined-link { text-decoration: underline; }
|
||||
.hint { font-style: italic; color: $hint-color; }
|
||||
.light { color: $common-gray; }
|
||||
|
||||
|
|
|
@ -14,6 +14,5 @@
|
|||
|
||||
&:hover {
|
||||
background-color: $user-mention-bg-hover;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
|
||||
.ref-name {
|
||||
font-size: 12px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color: $gl-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,10 @@
|
|||
padding: 6px 10px;
|
||||
border-radius: $label-border-radius;
|
||||
}
|
||||
|
||||
&:hover .color-label {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&.has-labels {
|
||||
|
@ -174,6 +178,14 @@
|
|||
color: $gray-darkest;
|
||||
}
|
||||
}
|
||||
|
||||
&.assignee {
|
||||
.author_link:hover {
|
||||
.author {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.block-first {
|
||||
|
@ -468,7 +480,6 @@
|
|||
a:not(.btn-retry) {
|
||||
&:hover {
|
||||
color: $md-link-color;
|
||||
text-decoration: none;
|
||||
|
||||
.avatar {
|
||||
border-color: rgba($avatar-border, .2);
|
||||
|
|
|
@ -208,7 +208,6 @@ ul.notes {
|
|||
|
||||
a {
|
||||
color: $gl-link-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
p {
|
||||
|
@ -395,6 +394,10 @@ ul.notes {
|
|||
&:focus,
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
|
||||
.note-header-author-name {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -461,6 +464,10 @@ ul.notes {
|
|||
.system-note-message {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
.profile-link-holder {
|
||||
display: inline;
|
||||
|
||||
a {
|
||||
a:not(.text-link) {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -724,6 +724,7 @@ a.deploy-project-label {
|
|||
&:hover,
|
||||
&:focus {
|
||||
color: $gl-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,7 +124,11 @@
|
|||
|
||||
&:hover,
|
||||
&.active {
|
||||
color: $black;
|
||||
text-decoration: none;
|
||||
|
||||
span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ class Projects::Clusters::UserController < Projects::ApplicationController
|
|||
def create
|
||||
@cluster = ::Clusters::CreateService
|
||||
.new(project, current_user, create_params)
|
||||
.execute(nil)
|
||||
.execute
|
||||
|
||||
if @cluster.persisted?
|
||||
redirect_to project_cluster_path(project, @cluster)
|
||||
|
|
|
@ -22,7 +22,7 @@ class Projects::ClustersController < Projects::ApplicationController
|
|||
def status
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
Gitlab::PollingInterval.set_header(response, interval: 10_000)
|
||||
Gitlab::PollingInterval.set_header(response, interval: STATUS_POLLING_INTERVAL)
|
||||
|
||||
render json: ClusterSerializer
|
||||
.new(project: @project, current_user: @current_user)
|
||||
|
@ -70,7 +70,8 @@ class Projects::ClustersController < Projects::ApplicationController
|
|||
private
|
||||
|
||||
def cluster
|
||||
@cluster ||= project.clusters.find_by(id: params[:id])&.present(current_user: current_user) || render_404
|
||||
@cluster ||= project.clusters.find(params[:id])
|
||||
.present(current_user: current_user)
|
||||
end
|
||||
|
||||
def create_params
|
||||
|
|
|
@ -25,7 +25,7 @@ class UsersFinder
|
|||
end
|
||||
|
||||
def execute
|
||||
users = User.all
|
||||
users = User.all.order_id_desc
|
||||
users = by_username(users)
|
||||
users = by_search(users)
|
||||
users = by_blocked(users)
|
||||
|
|
|
@ -63,7 +63,7 @@ module CommitsHelper
|
|||
# Returns a link formatted as a commit branch link
|
||||
def commit_branch_link(url, text)
|
||||
link_to(url, class: 'label label-gray ref-name branch-link') do
|
||||
icon('code-fork') + " #{text}"
|
||||
icon('code-fork', class: 'append-right-5') + "#{text}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -77,7 +77,7 @@ module CommitsHelper
|
|||
# Returns a link formatted as a commit tag link
|
||||
def commit_tag_link(url, text)
|
||||
link_to(url, class: 'label label-gray ref-name') do
|
||||
icon('tag') + " #{text}"
|
||||
icon('tag', class: 'append-right-5') + "#{text}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -113,7 +113,13 @@ module MarkupHelper
|
|||
text = wiki_page.content
|
||||
return '' unless text.present?
|
||||
|
||||
context = { pipeline: :wiki, project: @project, project_wiki: @project_wiki, page_slug: wiki_page.slug }
|
||||
context = {
|
||||
pipeline: :wiki,
|
||||
project: @project,
|
||||
project_wiki: @project_wiki,
|
||||
page_slug: wiki_page.slug,
|
||||
issuable_state_filter_enabled: true
|
||||
}
|
||||
|
||||
html =
|
||||
case wiki_page.format
|
||||
|
|
|
@ -97,11 +97,6 @@ module Clusters
|
|||
return false
|
||||
end
|
||||
|
||||
if managed? && name_changed?
|
||||
errors.add(:base, "cannot modify cluster name")
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -289,6 +289,14 @@ class Group < Namespace
|
|||
"#{parent.full_path}/#{path_was}"
|
||||
end
|
||||
|
||||
def group_member(user)
|
||||
if group_members.loaded?
|
||||
group_members.find { |gm| gm.user_id == user.id }
|
||||
else
|
||||
group_members.find_by(user_id: user)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_two_factor_requirement
|
||||
|
|
|
@ -139,7 +139,17 @@ class Namespace < ActiveRecord::Base
|
|||
def find_fork_of(project)
|
||||
return nil unless project.fork_network
|
||||
|
||||
project.fork_network.find_forks_in(projects).first
|
||||
if RequestStore.active?
|
||||
forks_in_namespace = RequestStore.fetch("namespaces:#{id}:forked_projects") do
|
||||
Hash.new do |found_forks, project|
|
||||
found_forks[project] = project.fork_network.find_forks_in(projects).first
|
||||
end
|
||||
end
|
||||
|
||||
forks_in_namespace[project]
|
||||
else
|
||||
project.fork_network.find_forks_in(projects).first
|
||||
end
|
||||
end
|
||||
|
||||
def lfs_enabled?
|
||||
|
|
|
@ -562,8 +562,7 @@ class Project < ActiveRecord::Base
|
|||
if forked?
|
||||
RepositoryForkWorker.perform_async(id,
|
||||
forked_from_project.repository_storage_path,
|
||||
forked_from_project.full_path,
|
||||
self.namespace.full_path)
|
||||
forked_from_project.disk_path)
|
||||
else
|
||||
RepositoryImportWorker.perform_async(self.id)
|
||||
end
|
||||
|
@ -1114,7 +1113,11 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def project_member(user)
|
||||
project_members.find_by(user_id: user)
|
||||
if project_members.loaded?
|
||||
project_members.find { |member| member.user_id == user.id }
|
||||
else
|
||||
project_members.find_by(user_id: user)
|
||||
end
|
||||
end
|
||||
|
||||
def default_branch
|
||||
|
|
|
@ -256,7 +256,7 @@ class Repository
|
|||
end
|
||||
|
||||
def diverging_commit_counts(branch)
|
||||
root_ref_hash = raw_repository.rev_parse_target(root_ref).oid
|
||||
root_ref_hash = raw_repository.commit(root_ref).id
|
||||
cache.fetch(:"diverging_commit_counts_#{branch.name}") do
|
||||
# Rugged seems to throw a `ReferenceError` when given branch_names rather
|
||||
# than SHA-1 hashes
|
||||
|
|
|
@ -487,7 +487,11 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def two_factor_u2f_enabled?
|
||||
u2f_registrations.exists?
|
||||
if u2f_registrations.loaded?
|
||||
u2f_registrations.any?
|
||||
else
|
||||
u2f_registrations.exists?
|
||||
end
|
||||
end
|
||||
|
||||
def namespace_uniq
|
||||
|
@ -998,7 +1002,11 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def notification_settings_for(source)
|
||||
notification_settings.find_or_initialize_by(source: source)
|
||||
if notification_settings.loaded?
|
||||
notification_settings.find { |notification| notification.source == source }
|
||||
else
|
||||
notification_settings.find_or_initialize_by(source: source)
|
||||
end
|
||||
end
|
||||
|
||||
# Lazy load global notification setting
|
||||
|
|
|
@ -12,8 +12,12 @@ class BaseCountService
|
|||
Rails.cache.fetch(cache_key, cache_options) { uncached_count }.to_i
|
||||
end
|
||||
|
||||
def refresh_cache
|
||||
Rails.cache.write(cache_key, uncached_count, raw: raw?)
|
||||
def count_stored?
|
||||
Rails.cache.read(cache_key).present?
|
||||
end
|
||||
|
||||
def refresh_cache(&block)
|
||||
Rails.cache.write(cache_key, block_given? ? yield : uncached_count, raw: raw?)
|
||||
end
|
||||
|
||||
def uncached_count
|
||||
|
|
|
@ -2,7 +2,7 @@ module Clusters
|
|||
class CreateService < BaseService
|
||||
attr_reader :access_token
|
||||
|
||||
def execute(access_token)
|
||||
def execute(access_token = nil)
|
||||
@access_token = access_token
|
||||
|
||||
raise Exception.new('Instance does not support multiple clusters') unless can_create_cluster?
|
||||
|
|
|
@ -98,6 +98,12 @@ module NotificationRecipientService
|
|||
self << [target.participants(user), :participating]
|
||||
end
|
||||
|
||||
def add_mentions(user, target:)
|
||||
return unless target.respond_to?(:mentioned_users)
|
||||
|
||||
self << [target.mentioned_users(user), :mention]
|
||||
end
|
||||
|
||||
# Get project/group users with CUSTOM notification level
|
||||
def add_custom_notifications
|
||||
user_ids = []
|
||||
|
@ -227,6 +233,11 @@ module NotificationRecipientService
|
|||
add_subscribed_users
|
||||
|
||||
if [:new_issue, :new_merge_request].include?(custom_action)
|
||||
# These will all be participants as well, but adding with the :mention
|
||||
# type ensures that users with the mention notification level will
|
||||
# receive them, too.
|
||||
add_mentions(current_user, target: target)
|
||||
|
||||
add_labels_subscribers
|
||||
end
|
||||
end
|
||||
|
@ -263,7 +274,7 @@ module NotificationRecipientService
|
|||
def build!
|
||||
# Add all users participating in the thread (author, assignee, comment authors)
|
||||
add_participants(note.author)
|
||||
self << [note.mentioned_users, :mention]
|
||||
add_mentions(note.author, target: note)
|
||||
|
||||
unless note.for_personal_snippet?
|
||||
# Merge project watchers
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# Service class for getting and caching the number of elements of several projects
|
||||
# Warning: do not user this service with a really large set of projects
|
||||
# because the service use maps to retrieve the project ids.
|
||||
module Projects
|
||||
class BatchCountService
|
||||
def initialize(projects)
|
||||
@projects = projects
|
||||
end
|
||||
|
||||
def refresh_cache
|
||||
@projects.each do |project|
|
||||
service = count_service.new(project)
|
||||
unless service.count_stored?
|
||||
service.refresh_cache { global_count[project.id].to_i }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def project_ids
|
||||
@projects.map(&:id)
|
||||
end
|
||||
|
||||
def global_count(project)
|
||||
raise NotImplementedError, 'global_count must be implemented and return an hash indexed by the project id'
|
||||
end
|
||||
|
||||
def count_service
|
||||
raise NotImplementedError, 'count_service must be implemented and return a Projects::CountService object'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
# Service class for getting and caching the number of forks of several projects
|
||||
# Warning: do not user this service with a really large set of projects
|
||||
# because the service use maps to retrieve the project ids
|
||||
module Projects
|
||||
class BatchForksCountService < Projects::BatchCountService
|
||||
def global_count
|
||||
@global_count ||= begin
|
||||
count_service.query(project_ids)
|
||||
.group(:forked_from_project_id)
|
||||
.count
|
||||
end
|
||||
end
|
||||
|
||||
def count_service
|
||||
::Projects::ForksCountService
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
# Service class for getting and caching the number of issues of several projects
|
||||
# Warning: do not user this service with a really large set of projects
|
||||
# because the service use maps to retrieve the project ids
|
||||
module Projects
|
||||
class BatchOpenIssuesCountService < Projects::BatchCountService
|
||||
def global_count
|
||||
@global_count ||= begin
|
||||
count_service.query(project_ids).group(:project_id).count
|
||||
end
|
||||
end
|
||||
|
||||
def count_service
|
||||
::Projects::OpenIssuesCountService
|
||||
end
|
||||
end
|
||||
end
|
|
@ -11,6 +11,10 @@ module Projects
|
|||
@project = project
|
||||
end
|
||||
|
||||
def relation_for_count
|
||||
self.class.query(@project.id)
|
||||
end
|
||||
|
||||
def cache_key_name
|
||||
raise(
|
||||
NotImplementedError,
|
||||
|
@ -21,5 +25,12 @@ module Projects
|
|||
def cache_key
|
||||
['projects', 'count_service', VERSION, @project.id, cache_key_name]
|
||||
end
|
||||
|
||||
def self.query(project_ids)
|
||||
raise(
|
||||
NotImplementedError,
|
||||
'"query" must be implemented and return an ActiveRecord::Relation'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
module Projects
|
||||
# Service class for getting and caching the number of forks of a project.
|
||||
class ForksCountService < Projects::CountService
|
||||
def relation_for_count
|
||||
@project.forks
|
||||
end
|
||||
|
||||
def cache_key_name
|
||||
'forks_count'
|
||||
end
|
||||
|
||||
def self.query(project_ids)
|
||||
# We can't directly change ForkedProjectLink to ForkNetworkMember here
|
||||
# Nowadays, when a call using v3 to projects/:id/fork is made,
|
||||
# the relationship to ForkNetworkMember is not updated
|
||||
ForkedProjectLink.where(forked_from_project: project_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,14 +2,14 @@ module Projects
|
|||
# Service class for counting and caching the number of open issues of a
|
||||
# project.
|
||||
class OpenIssuesCountService < Projects::CountService
|
||||
def relation_for_count
|
||||
# We don't include confidential issues in this number since this would
|
||||
# expose the number of confidential issues to non project members.
|
||||
@project.issues.opened.public_only
|
||||
end
|
||||
|
||||
def cache_key_name
|
||||
'open_issues_count'
|
||||
end
|
||||
|
||||
def self.query(project_ids)
|
||||
# We don't include confidential issues in this number since this would
|
||||
# expose the number of confidential issues to non project members.
|
||||
Issue.opened.public_only.where(project: project_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,11 +3,7 @@
|
|||
# Custom validator for ClusterName.
|
||||
class ClusterNameValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
if record.user?
|
||||
unless value.present?
|
||||
record.errors.add(attribute, " has to be present")
|
||||
end
|
||||
elsif record.gcp?
|
||||
if record.managed?
|
||||
if record.persisted? && record.name_changed?
|
||||
record.errors.add(attribute, " can not be changed because it's synchronized with provider")
|
||||
end
|
||||
|
@ -19,6 +15,10 @@ class ClusterNameValidator < ActiveModel::EachValidator
|
|||
unless value =~ Gitlab::Regex.kubernetes_namespace_regex
|
||||
record.errors.add(attribute, Gitlab::Regex.kubernetes_namespace_regex_message)
|
||||
end
|
||||
else
|
||||
unless value.present?
|
||||
record.errors.add(attribute, " has to be present")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
= render 'devise/shared/tab_single', tab_title:'Change your password'
|
||||
.login-box
|
||||
.login-body
|
||||
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'gl-show-field-errors' }) do |f|
|
||||
= form_for(resource, as: resource_name, url: password_path(:user), html: { method: :put, class: 'gl-show-field-errors' }) do |f|
|
||||
.devise-errors
|
||||
= devise_error_messages!
|
||||
= f.hidden_field :reset_password_token
|
||||
|
@ -17,5 +17,5 @@
|
|||
.clearfix.prepend-top-20
|
||||
%p
|
||||
%span.light Didn't receive a confirmation email?
|
||||
= link_to "Request a new one", new_confirmation_path(resource_name)
|
||||
= link_to "Request a new one", new_confirmation_path(:user)
|
||||
= render 'devise/shared/sign_in_link'
|
||||
|
|
|
@ -11,6 +11,6 @@
|
|||
= f.check_box :remember_me, class: 'remember-me-checkbox'
|
||||
%span Remember me
|
||||
.pull-right.forgot-password
|
||||
= link_to "Forgot your password?", new_password_path(resource_name)
|
||||
= link_to "Forgot your password?", new_password_path(:user)
|
||||
.submit-container.move-submit-down
|
||||
= f.submit "Sign in", class: "btn btn-save"
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<%- if controller_name != 'sessions' %>
|
||||
<%= link_to "Sign in", new_session_path(resource_name), class: "btn" %><br />
|
||||
<%= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: "btn" %><br />
|
||||
<% end -%>
|
||||
|
||||
<%- if devise_mapping.registerable? && controller_name != 'registrations' && allow_signup? %>
|
||||
<%= link_to "Sign up", new_registration_path(resource_name) %><br />
|
||||
<%= link_to "Sign up", new_registration_path(:user) %><br />
|
||||
<% end -%>
|
||||
|
||||
<%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
|
||||
<%= link_to "Forgot your password?", new_password_path(resource_name), class: "btn" %><br />
|
||||
<%= link_to "Forgot your password?", new_password_path(:user), class: "btn" %><br />
|
||||
<% end -%>
|
||||
|
||||
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
|
||||
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
|
||||
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(:user) %><br />
|
||||
<% end -%>
|
||||
|
||||
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
|
||||
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
|
||||
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(:user) %><br />
|
||||
<% end -%>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
%p
|
||||
%span.light
|
||||
Already have login and password?
|
||||
= link_to "Sign in", new_session_path(resource_name)
|
||||
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes')
|
||||
|
|
|
@ -31,4 +31,4 @@
|
|||
%p
|
||||
%span.light Didn't receive a confirmation email?
|
||||
= succeed '.' do
|
||||
= link_to "Request a new one", new_confirmation_path(resource_name)
|
||||
= link_to "Request a new one", new_confirmation_path(:user)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
%p Try logging in using your username or email. If you have forgotten your password, try recovering it
|
||||
|
||||
= link_to "Sign in", new_session_path(:user), class: 'btn primary'
|
||||
= link_to "Recover password", new_password_path(resource_name), class: 'btn secondary'
|
||||
= link_to "Recover password", new_password_path(:user), class: 'btn secondary'
|
||||
|
||||
%hr
|
||||
%p.light If none of the options work, try contacting a GitLab administrator.
|
||||
|
|
|
@ -19,5 +19,5 @@
|
|||
distributed with computer software, forming part of its documentation.
|
||||
%p
|
||||
We recommend you to
|
||||
= link_to "add a README", add_special_file_path(@project, file_name: 'README.md'), class: 'underlined-link'
|
||||
= link_to "add a README", add_special_file_path(@project, file_name: 'README.md')
|
||||
file to the repository and GitLab will render it here instead of this message.
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
%tr.tree-item{ 'data-link' => path_to_directory }
|
||||
%td.tree-item-file-name
|
||||
= tree_icon('folder', '755', directory.name)
|
||||
= link_to path_to_directory do
|
||||
%span.str-truncated= directory.name
|
||||
= link_to path_to_directory, class: 'str-truncated' do
|
||||
%span= directory.name
|
||||
%td
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
%td.tree-item-file-name
|
||||
= tree_icon('file', blob.mode, blob.name)
|
||||
- if external_link
|
||||
= link_to path_to_file, class: 'tree-item-file-external-link js-artifact-tree-tooltip',
|
||||
= link_to path_to_file, class: 'tree-item-file-external-link js-artifact-tree-tooltip str-truncated',
|
||||
target: '_blank', rel: 'noopener noreferrer', title: _('Opens in a new window') do
|
||||
%span.str-truncated>= blob.name
|
||||
%span>= blob.name
|
||||
= icon('external-link', class: 'js-artifact-tree-external-icon')
|
||||
- else
|
||||
= link_to path_to_file do
|
||||
%span.str-truncated= blob.name
|
||||
= link_to path_to_file, class: 'str-truncated' do
|
||||
%span= blob.name
|
||||
%td
|
||||
= number_to_human_size(blob.size, precision: 2)
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
%li{ class: "js-branch-#{branch.name}" }
|
||||
%div
|
||||
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do
|
||||
= icon('code-fork')
|
||||
= branch.name
|
||||
= icon('code-fork', class: 'append-right-5') + "#{branch.name}"
|
||||
|
||||
- if branch.name == @repository.root_ref
|
||||
%span.label.label-primary default
|
||||
|
|
|
@ -41,6 +41,6 @@
|
|||
- if commit.status(ref)
|
||||
= render_commit_status(commit, ref: ref)
|
||||
|
||||
= link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent"
|
||||
= link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent btn-link"
|
||||
= clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"))
|
||||
= link_to_browse_code(project, commit)
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
%p
|
||||
Otherwise you can start with adding a
|
||||
= succeed ',' do
|
||||
= link_to "README", add_special_file_path(@project, file_name: 'README.md'), class: 'underlined-link'
|
||||
= link_to "README", add_special_file_path(@project, file_name: 'README.md')
|
||||
a
|
||||
= succeed ',' do
|
||||
= link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link'
|
||||
= link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE')
|
||||
or a
|
||||
= link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link'
|
||||
= link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore')
|
||||
to this project.
|
||||
%p
|
||||
You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected.
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
%li prevent pushes from everybody except Masters
|
||||
%li prevent <strong>anyone</strong> from force pushing to the branch
|
||||
%li prevent <strong>anyone</strong> from deleting the branch
|
||||
%p Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}.
|
||||
%p Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches")} and #{link_to "project permissions", help_page_path("user/permissions")}.
|
||||
|
||||
- if can? current_user, :admin_project, @project
|
||||
= content_for :create_protected_branch
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
%li Prevent <strong>anyone</strong> from updating the tag
|
||||
%li Prevent <strong>anyone</strong> from deleting the tag
|
||||
|
||||
%p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags"), class: "underlined-link"}.
|
||||
%p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags")}.
|
||||
|
||||
- if can? current_user, :admin_project, @project
|
||||
= yield :create_protected_tag
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
%td.tree-item-file-name
|
||||
= tree_icon(type, blob_item.mode, blob_item.name)
|
||||
- file_name = blob_item.name
|
||||
= link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), title: file_name do
|
||||
%span.str-truncated= file_name
|
||||
= link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do
|
||||
%span= file_name
|
||||
%td.hidden-xs.tree-commit
|
||||
%td.tree-time-ago.cgray.text-right
|
||||
= render 'projects/tree/spinner'
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
%td.tree-item-file-name
|
||||
= tree_icon(type, tree_item.mode, tree_item.name)
|
||||
- path = flatten_tree(@path, tree_item)
|
||||
= link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), title: path do
|
||||
%span.str-truncated= path
|
||||
= link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), class: 'str-truncated', title: path do
|
||||
%span= path
|
||||
%td.hidden-xs.tree-commit
|
||||
%td.tree-time-ago.text-right
|
||||
= render 'projects/tree/spinner'
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
|
||||
- git_access_url = project_wikis_git_access_path(@project)
|
||||
= link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '' do
|
||||
= succeed ' ' do
|
||||
= icon('cloud-download')
|
||||
= _("Clone repository")
|
||||
= icon('cloud-download', class: 'append-right-5')
|
||||
%span= _("Clone repository")
|
||||
|
||||
.blocks-container
|
||||
.block.block-first
|
||||
|
|
|
@ -38,9 +38,9 @@
|
|||
= link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: {confirm: 'Remove this label? Are you sure?'}
|
||||
|
||||
.pull-right.hidden-xs.hidden-sm.hidden-md
|
||||
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
|
||||
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action btn-link') do
|
||||
view merge requests
|
||||
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action') do
|
||||
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action btn-link') do
|
||||
view open issues
|
||||
|
||||
- if current_user
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
- type = local_assigns.fetch(:type, :issues)
|
||||
- page_context_word = type.to_s.humanize(capitalize: false)
|
||||
- issuables = @issues || @merge_requests
|
||||
|
||||
%ul.nav-links.issues-state-filters
|
||||
%li{ class: active_when(params[:state] == 'opened') }>
|
||||
|
@ -20,6 +19,4 @@
|
|||
= link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by issues that are currently closed.', data: { state: 'closed' } do
|
||||
#{issuables_state_counter_text(type, :closed)}
|
||||
|
||||
%li{ class: active_when(params[:state] == 'all') }>
|
||||
= link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}.", data: { state: 'all' } do
|
||||
#{issuables_state_counter_text(type, :all)}
|
||||
= render 'shared/issuable/nav_links/all', page_context_word: page_context_word, counter: issuables_state_counter_text(type, :all)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
- page_context_word = local_assigns.fetch(:page_context_word)
|
||||
- counter = local_assigns.fetch(:counter)
|
||||
|
||||
%li{ class: active_when(params[:state] == 'all') }>
|
||||
= link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}.", data: { state: 'all' } do
|
||||
#{counter}
|
|
@ -31,8 +31,7 @@
|
|||
.note-header
|
||||
.note-header-info
|
||||
%a{ href: user_path(note.author) }
|
||||
%span.note-header-author-name
|
||||
= sanitize(note.author.name)
|
||||
%span.note-header-author-name= sanitize(note.author.name)
|
||||
%span.note-headline-light
|
||||
= note.author.to_reference
|
||||
%span.note-headline-light
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
.cover-desc
|
||||
- unless @user.public_email.blank?
|
||||
.profile-link-holder.middle-dot-divider
|
||||
= link_to @user.public_email, "mailto:#{@user.public_email}"
|
||||
= link_to @user.public_email, "mailto:#{@user.public_email}", class: 'text-link'
|
||||
- unless @user.skype.blank?
|
||||
.profile-link-holder.middle-dot-divider
|
||||
= link_to "skype:#{@user.skype}", title: "Skype" do
|
||||
|
@ -66,7 +66,7 @@
|
|||
= icon('twitter-square')
|
||||
- unless @user.website_url.blank?
|
||||
.profile-link-holder.middle-dot-divider
|
||||
= link_to @user.short_website_url, @user.full_website_url
|
||||
= link_to @user.short_website_url, @user.full_website_url, class: 'text-link'
|
||||
- unless @user.location.blank?
|
||||
.profile-link-holder.middle-dot-divider
|
||||
= icon('map-marker')
|
||||
|
|
|
@ -32,16 +32,14 @@ module RepositoryCheck
|
|||
end
|
||||
|
||||
def git_fsck(repository)
|
||||
path = repository.path_to_repo
|
||||
cmd = %W(nice git --git-dir=#{path} fsck)
|
||||
output, status = Gitlab::Popen.popen(cmd)
|
||||
return false unless repository.exists?
|
||||
|
||||
if status.zero?
|
||||
true
|
||||
else
|
||||
Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}")
|
||||
false
|
||||
end
|
||||
repository.raw_repository.fsck
|
||||
|
||||
true
|
||||
rescue Gitlab::Git::Repository::GitError => e
|
||||
Gitlab::RepositoryCheckLogger.error(e.message)
|
||||
false
|
||||
end
|
||||
|
||||
def has_pushes?(project)
|
||||
|
|
|
@ -8,18 +8,18 @@ class RepositoryForkWorker
|
|||
|
||||
sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION
|
||||
|
||||
def perform(project_id, forked_from_repository_storage_path, source_path, target_path)
|
||||
def perform(project_id, forked_from_repository_storage_path, source_disk_path)
|
||||
project = Project.find(project_id)
|
||||
|
||||
return unless start_fork(project)
|
||||
|
||||
Gitlab::Metrics.add_event(:fork_repository,
|
||||
source_path: source_path,
|
||||
target_path: target_path)
|
||||
source_path: source_disk_path,
|
||||
target_path: project.disk_path)
|
||||
|
||||
result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_path,
|
||||
project.repository_storage_path, target_path)
|
||||
raise ForkError, "Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}" unless result
|
||||
result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_disk_path,
|
||||
project.repository_storage_path, project.disk_path)
|
||||
raise ForkError, "Unable to fork project #{project_id} for repository #{source_disk_path} -> #{project.disk_path}" unless result
|
||||
|
||||
project.repository.after_import
|
||||
raise ForkError, "Project #{project_id} had an invalid repository after fork" unless project.valid_repo?
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Create a new form to add Existing Kubernetes Cluster
|
||||
merge_request: 14805
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Fix sending notification emails to users with the mention level set who were
|
||||
mentioned in an issue or merge request description
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Confirming email with invalid token should no longer generate an error
|
||||
merge_request: 15726
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add axios to common file
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: show status of gitlab reference links in wiki
|
||||
merge_request: 15694
|
||||
author: haseebeqx
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix the fork project functionality for projects with hashed storage
|
||||
merge_request: 15671
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix typo in docs about Elasticsearch
|
||||
merge_request: 15699
|
||||
author: Takuya Noguchi
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Keep track of all circuitbreaker keys in a set
|
||||
merge_request: 15613
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Reduce requests for project forks on show page of projects that have forks
|
||||
merge_request: 15663
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Added default order to UsersFinder
|
||||
merge_request: 15679
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Optimize API /groups/:id/projects by preloading associations
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use custom user agent header in all GCP API requests.
|
||||
merge_request: 15705
|
||||
author:
|
||||
type: changed
|
|
@ -1,3 +1,7 @@
|
|||
# WARNING changes in this file must be manually propagated to gitaly-ruby.
|
||||
#
|
||||
# https://gitlab.com/gitlab-org/gitaly/blob/master/ruby/lib/gitlab/gollum.rb
|
||||
|
||||
module Gollum
|
||||
GIT_ADAPTER = "rugged".freeze
|
||||
end
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
require './spec/support/sidekiq'
|
||||
|
||||
Gitlab::Seeder.quiet do
|
||||
User.seed do |s|
|
||||
s.id = 1
|
||||
s.name = 'Administrator'
|
||||
s.email = 'admin@example.com'
|
||||
s.notification_email = 'admin@example.com'
|
||||
s.username = 'root'
|
||||
s.password = '5iveL!fe'
|
||||
s.admin = true
|
||||
s.projects_limit = 100
|
||||
s.confirmed_at = DateTime.now
|
||||
end
|
||||
end
|
|
@ -80,7 +80,7 @@ changes.
|
|||
|
||||
The first example focuses on _how_ we fixed something, not on _what_ it fixes.
|
||||
The rewritten version clearly describes the _end benefit_ to the user (fewer 500
|
||||
errors), and _when_ (searching commits with ElasticSearch).
|
||||
errors), and _when_ (searching commits with Elasticsearch).
|
||||
|
||||
Use your best judgement and try to put yourself in the mindset of someone
|
||||
reading the compiled changelog. Does this entry add value? Does it offer context
|
||||
|
|
|
@ -170,12 +170,6 @@ You can combine one or more of the following:
|
|||
= link_to 'Help page', help_page_path('user/permissions'), class: 'btn btn-info'
|
||||
```
|
||||
|
||||
1. **Underlining a link.**
|
||||
|
||||
```haml
|
||||
= link_to 'Help page', help_page_path('user/permissions'), class: 'underlined-link'
|
||||
```
|
||||
|
||||
1. **Using links inline of some text.**
|
||||
|
||||
```haml
|
||||
|
|
|
@ -152,12 +152,23 @@ CE and EE.
|
|||
## Previewing the changes live
|
||||
|
||||
If you want to preview the doc changes of your merge request live, you can use
|
||||
the manual `review-docs-deploy` job in your merge request.
|
||||
the manual `review-docs-deploy` job in your merge request. You will need at
|
||||
least Master permissions to be able to run it and is currently enabled for the
|
||||
following projects:
|
||||
|
||||
- https://gitlab.com/gitlab-org/gitlab-ce
|
||||
- https://gitlab.com/gitlab-org/gitlab-ee
|
||||
|
||||
NOTE: **Note:**
|
||||
You will need to push a branch to those repositories, it doesn't work for forks.
|
||||
|
||||
TIP: **Tip:**
|
||||
If your branch contains only documentation changes, you can use
|
||||
[special branch names](#testing) to avoid long running pipelines.
|
||||
|
||||
In the mini pipeline graph, you should see an `>>` icon. Clicking on it will
|
||||
reveal the `review-docs-deploy` job. Hit the play button for the job to start.
|
||||
|
||||
![Manual trigger a docs build](img/manual_build_docs.png)
|
||||
|
||||
This job will:
|
||||
|
|
|
@ -175,7 +175,7 @@ A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of
|
|||
|
||||
### EC2 Instance
|
||||
|
||||
### ElasticSearch
|
||||
### Elasticsearch
|
||||
|
||||
Elasticsearch is a flexible, scalable and powerful search service. When [enabled](https://gitlab.com/help/integration/elasticsearch.md), it helps keep GitLab's search fast when dealing with a huge amount of data.
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue