Commit 0b899366 authored by Riccardo Padovani's avatar Riccardo Padovani

Merge branch 'master' into patch-4

parents 0548428b b5527d26
Please view this file on the master branch, on stable branches it's out of date.
## 11.4.5 (2018-11-04)
### Fixed (1 change)
- Stops showing review actions on commit discussions in merge requests. !8007
### Performance (1 change)
- Add indexes to all geo event foreign keys. !7990
## 11.4.4 (2018-10-30)
- No changes.
......
......@@ -2,6 +2,21 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 11.4.5 (2018-11-04)
### Fixed (5 changes, 1 of them is from the community)
- Fix stuck job warning message. !8060
- fix link to enable usage ping from convdev index. !22545 (Anand Capur)
- Update gitlab-ui dependency to 1.8.0-hotfix.1 to fix IE11 bug.
- Remove duplicate escape in job sidebar.
- Fixed merge request fill tree toggling not respecting fluid width preference.
### Other (1 change)
- Fix stage dropdown not rendering in different languages.
## 11.4.4 (2018-10-30)
### Security (1 change)
......
import Vue from 'vue';
import { GlLoadingIcon, GlTooltipDirective } from '@gitlab-org/gitlab-ui';
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
Vue.component('gl-loading-icon', GlLoadingIcon);
Vue.directive('gl-tooltip', GlTooltipDirective);
......@@ -3,6 +3,7 @@ import { mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import { pluralize, truncate } from '~/lib/utils/text_utility';
import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
import { GlTooltipDirective } from '@gitlab-org/gitlab-ui';
import { COUNT_OF_AVATARS_IN_GUTTER, LENGTH_OF_AVATAR_TOOLTIP } from '../constants';
export default {
......@@ -10,6 +11,9 @@ export default {
Icon,
UserAvatarImage,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
discussions: {
type: Array,
......
......@@ -167,7 +167,7 @@ export default {
<button
v-if="shouldShowCommentButton"
type="button"
class="add-diff-note js-add-diff-note-button"
class="add-diff-note js-add-diff-note-button qa-diff-comment"
title="Add a comment to this line"
@click="handleCommentButton"
>
......
......@@ -102,7 +102,7 @@ export default {
:line-type="newLineType"
:is-bottom="isBottom"
:is-hover="isHover"
class="diff-line-num new_line"
class="diff-line-num new_line qa-new-diff-line"
/>
<td
:class="line.type"
......
<script>
import { s__, sprintf } from '~/locale';
import { formatTime } from '~/lib/utils/datetime_utility';
import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '../event_hub';
import tooltip from '../../vue_shared/directives/tooltip';
......@@ -28,10 +30,24 @@ export default {
},
},
methods: {
onClickAction(endpoint) {
onClickAction(action) {
if (action.scheduledAt) {
const confirmationMessage = sprintf(
s__(
"DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes.",
),
{ jobName: action.name },
);
// https://gitlab.com/gitlab-org/gitlab-ce/issues/52156
// eslint-disable-next-line no-alert
if (!window.confirm(confirmationMessage)) {
return;
}
}
this.isLoading = true;
eventHub.$emit('postAction', { endpoint });
eventHub.$emit('postAction', { endpoint: action.playPath });
},
isActionDisabled(action) {
......@@ -41,6 +57,11 @@ export default {
return !action.playable;
},
remainingTime(action) {
const remainingMilliseconds = new Date(action.scheduledAt).getTime() - Date.now();
return formatTime(Math.max(0, remainingMilliseconds));
},
},
};
</script>
......@@ -54,7 +75,7 @@ export default {
:aria-label="title"
:disabled="isLoading"
type="button"
class="dropdown btn btn-default dropdown-new js-dropdown-play-icon-container"
class="dropdown btn btn-default dropdown-new js-environment-actions-dropdown"
data-container="body"
data-toggle="dropdown"
>
......@@ -75,12 +96,19 @@ export default {
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
type="button"
class="js-manual-action-link no-btn btn"
@click="onClickAction(action.play_path)"
class="js-manual-action-link no-btn btn d-flex align-items-center"
@click="onClickAction(action)"
>
<span>
<span class="flex-fill">
{{ action.name }}
</span>
<span
v-if="action.scheduledAt"
class="text-secondary"
>
<icon name="clock" />
{{ remainingTime(action) }}
</span>
</button>
</li>
</ul>
......
......@@ -13,6 +13,7 @@ import TerminalButtonComponent from './environment_terminal_button.vue';
import MonitoringButtonComponent from './environment_monitoring.vue';
import CommitComponent from '../../vue_shared/components/commit.vue';
import eventHub from '../event_hub';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
/**
* Environment Item Component
......@@ -73,21 +74,6 @@ export default {
return false;
},
/**
* Verifies is the given environment has manual actions.
* Used to verify if we should render them or nor.
*
* @returns {Boolean|Undefined}
*/
hasManualActions() {
return (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.manual_actions &&
this.model.last_deployment.manual_actions.length > 0
);
},
/**
* Checkes whether the environment is protected.
* (`is_protected` currently only set in EE)
......@@ -154,23 +140,20 @@ export default {
return '';
},
/**
* Returns the manual actions with the name parsed.
*
* @returns {Array.<Object>|Undefined}
*/
manualActions() {
if (this.hasManualActions) {
return this.model.last_deployment.manual_actions.map(action => {
const parsedAction = {
name: humanize(action.name),
play_path: action.play_path,
playable: action.playable,
};
return parsedAction;
});
}
actions() {
if (!this.model || !this.model.last_deployment || !this.canCreateDeployment) {
return [];
}
const { manualActions, scheduledActions } = convertObjectPropsToCamelCase(
this.model.last_deployment,
{ deep: true },
);
const combinedActions = (manualActions || []).concat(scheduledActions || []);
return combinedActions.map(action => ({
...action,
name: humanize(action.name),
}));
},
/**
......@@ -443,7 +426,7 @@ export default {
displayEnvironmentActions() {
return (
this.hasManualActions ||
this.actions.length > 0 ||
this.externalURL ||
this.monitoringUrl ||
this.canStopEnvironment ||
......@@ -641,8 +624,8 @@ export default {
/>
<actions-component
v-if="hasManualActions && canCreateDeployment"
:actions="manualActions"
v-if="actions.length > 0"
:actions="actions"
/>
<terminal-button-component
......
<script>
import { GlLink } from '@gitlab-org/gitlab-ui';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
export default {
components: {
TimeagoTooltip,
GlLink,
},
mixins: [timeagoMixin],
props: {
......@@ -53,16 +55,16 @@ export default {
class="btn-group d-flex"
role="group"
>
<a
<gl-link
v-if="artifact.keep_path"
:href="artifact.keep_path"
class="js-keep-artifacts btn btn-sm btn-default"
data-method="post"
>
{{ s__('Job|Keep') }}
</a>
</gl-link>
<a
<gl-link
v-if="artifact.download_path"
:href="artifact.download_path"
class="js-download-artifacts btn btn-sm btn-default"
......@@ -70,15 +72,15 @@ export default {
rel="nofollow"
>
{{ s__('Job|Download') }}
</a>
</gl-link>
<a
<gl-link
v-if="artifact.browse_path"
:href="artifact.browse_path"
class="js-browse-artifacts btn btn-sm btn-default"
>
{{ s__('Job|Browse') }}
</a>
</gl-link>
</div>
</div>
</template>
<script>
import { GlLink } from '@gitlab-org/gitlab-ui';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
export default {
components: {
ClipboardButton,
GlLink,
},
props: {
commit: {
......@@ -31,10 +33,10 @@ export default {
<p>
{{ __('Commit') }}
<a
<gl-link
:href="commit.commit_path"
class="js-commit-sha commit-sha link-commit"
>{{ commit.short_id }}</a>
>{{ commit.short_id }}</gl-link>
<clipboard-button
:text="commit.short_id"
......@@ -42,11 +44,11 @@ export default {
css-class="btn btn-clipboard btn-transparent"
/>
<a
<gl-link
v-if="mergeRequest"
:href="mergeRequest.path"
class="js-link-commit link-commit"
>!{{ mergeRequest.iid }}</a>
>!{{ mergeRequest.iid }}</gl-link>
</p>
<p class="build-light-text append-bottom-0">
......
<script>
import { GlLink } from '@gitlab-org/gitlab-ui';
export default {
components: {
GlLink,
},
props: {
illustrationPath: {
type: String,
......@@ -62,13 +67,13 @@ export default {
v-if="action"
class="text-center"
>
<a
<gl-link
:href="action.path"
:data-method="action.method"
class="js-job-empty-state-action btn btn-primary"
>
{{ action.button_title }}
</a>
</gl-link>
</div>
</div>
</div>
......
<script>
import _ from 'underscore';
import { GlLink } from '@gitlab-org/gitlab-ui';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
components: {
TimeagoTooltip,
GlLink,
},
props: {
user: {
......@@ -29,9 +31,9 @@ export default {
<div class="erased alert alert-warning">
<template v-if="isErasedByUser">
{{ s__("Job|Job has been erased by") }}
<a :href="user.web_url">
<gl-link :href="user.web_url">
{{ user.username }}
</a>
</gl-link>
</template>
<template v-else>
{{ s__("Job|Job has been erased") }}
......
<script>
import _ from 'underscore';
import { mapGetters, mapState, mapActions } from 'vuex';
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
import bp from '~/breakpoints';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
......@@ -26,6 +27,7 @@ export default {
EmptyState,
EnvironmentsBlock,
ErasedBlock,
GlLoadingIcon,
Log,
LogTopBar,
StuckBlock,
......
<script>
import { GlTooltipDirective, GlLink } from '@gitlab-org/gitlab-ui';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
components: {
CiIcon,
Icon,
GlLink,
},
directives: {
tooltip,
GlTooltip: GlTooltipDirective,
},
props: {
job: {
......@@ -37,11 +38,10 @@ export default {
active: isActive
}"
>
<a
v-tooltip
<gl-link
v-gl-tooltip
:href="job.status.details_path"
:title="tooltipText"
data-container="body"
data-boundary="viewport"
class="js-job-link"
>
......@@ -60,6 +60,6 @@ export default {
name="retry"
class="js-retry-icon"
/>
</a>
</gl-link>
</div>
</template>
<script>
import { GlTooltipDirective, GlLink, GlButton } from '@gitlab-org/gitlab-ui';
import { polyfillSticky } from '~/lib/utils/sticky';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { sprintf } from '~/locale';
import scrollDown from '../svg/scroll_down.svg';
......@@ -9,9 +9,11 @@ import scrollDown from '../svg/scroll_down.svg';
export default {
components: {
Icon,
GlLink,
GlButton,
},
directives: {
tooltip,
GlTooltip: GlTooltipDirective,
},
scrollDown,
props: {
......@@ -73,76 +75,70 @@ export default {
<template v-if="isTraceSizeVisible">
{{ jobLogSize }}
<a
<gl-link
v-if="rawPath"
:href="rawPath"
class="js-raw-link raw-link"
>
{{ s__("Job|Complete Raw") }}
</a>
</gl-link>
</template>
</div>
<!-- eo truncate information -->
<div class="controllers float-right">
<!-- links -->
<a
<gl-link
v-if="rawPath"
v-tooltip
v-gl-tooltip.body
:title="s__('Job|Show complete raw')"
:href="rawPath"
class="js-raw-link-controller controllers-buttons"
data-container="body"
>
<icon name="doc-text" />
</a>
</gl-link>
<a
<gl-link
v-if="erasePath"
v-tooltip
v-gl-tooltip.body
:title="s__('Job|Erase job log')"
:href="erasePath"
:data-confirm="__('Are you sure you want to erase this build?')"
class="js-erase-link controllers-buttons"
data-container="body"
data-method="post"
>
<icon name="remove" />
</a>
</gl-link>
<!-- eo links -->
<!-- scroll buttons -->
<div
v-tooltip
v-gl-tooltip
:title="s__('Job|Scroll to top')"
class="controllers-buttons"
data-container="body"
>
<button
<gl-button
:disabled="isScrollTopDisabled"
type="button"
class="js-scroll-top btn-scroll btn-transparent btn-blank"
@click="handleScrollToTop"
>
<icon name="scroll_up"/>
</button>
<icon name="scroll_up" />
</gl-button>
</div>
<div
v-tooltip
v-gl-tooltip
:title="s__('Job|Scroll to bottom')"
class="controllers-buttons"
data-container="body"
>
<button
<gl-button
:disabled="isScrollBottomDisabled"
type="button"
class="js-scroll-bottom btn-scroll btn-transparent btn-blank"
:class="{ animate: isScrollingDown }"
@click="handleScrollToBottom"
v-html="$options.scrollDown"
>
</button>
/>
</div>
<!-- eo scroll buttons -->
</div>
......
<script>
import { GlLink } from '@gitlab-org/gitlab-ui';
export default {
name: 'SidebarDetailRow',
components: {
GlLink,
},
props: {
title: {
type: String,
......@@ -41,7 +46,7 @@ export default {
v-if="hasHelpURL"
class="help-button float-right"
>
<a
<gl-link
:href="helpUrl"
target="_blank"
rel="noopener noreferrer nofollow"
......@@ -50,7 +55,7 @@ export default {
class="fa fa-question-circle"
aria-hidden="true"
></i>
</a>
</gl-link>
</span>
</p>
</template>
<script>
import { GlLink } from '@gitlab-org/gitlab-ui';
/**
* Renders Stuck Runners block for job's view.
*/
export default {
components: {
GlLink,
},
props: {
hasNoRunnersForProject: {
type: Boolean,
......@@ -52,12 +56,12 @@ export default {
</p>
{{ __("Go to") }}
<a
<gl-link
v-if="runnersPath"
:href="runnersPath"
class="js-runners-path"
>
{{ __("Runners page") }}
</a>
</gl-link>
</div>
</template>
......@@ -390,7 +390,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
:disabled="isSubmitButtonDisabled"
name="button"
type="button"
class="btn comment-btn note-type-toggle js-note-new-discussion dropdown-toggle"
class="btn comment-btn note-type-toggle js-note-new-discussion dropdown-toggle qa-note-dropdown"
data-display="static"
data-toggle="dropdown"
aria-label="Open comment type dropdown">
......@@ -422,7 +422,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
<li :class="{ 'droplab-item-selected': noteType === 'discussion' }">
<button
type="button"
class="btn btn-transparent"
class="btn btn-transparent qa-discussion-option"
@click.prevent="setNoteType('discussion')">
<i
aria-hidden="true"
......
<script>
import $ from 'jquery';
import Icon from '~/vue_shared/components/icon.vue';
import { mapGetters, mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import { DISCUSSION_FILTERS_DEFAULT_VALUE, HISTORY_ONLY_FILTER_VALUE } from '../constants';
export default {
components: {
......@@ -12,14 +13,17 @@ export default {
type: Array,
required: true,
},
defaultValue: {
selectedValue: {
type: Number,
default: null,
required: false,
},
},
data() {
return { currentValue: this.defaultValue };
return {
currentValue: this.selectedValue,
defaultValue: DISCUSSION_FILTERS_DEFAULT_VALUE,
};
},
computed: {
...mapGetters(['getNotesDataByProp']),
......@@ -28,8 +32,11 @@ export default {
return this.filters.find(filter => filter.value === this.currentValue);
},
},
mounted() {
this.toggleCommentsForm();
},
methods: {
...mapActions(['filterDiscussion']),
...mapActions(['filterDiscussion', 'setCommentsDisabled']),
selectFilter(value) {
const filter = parseInt(value, 10);
......@@ -39,6 +46,10 @@ export default {
if (filter === this.currentValue) return;
this.currentValue = filter;
this.filterDiscussion({ path: this.getNotesDataByProp('discussionsPath'), filter });
this.toggleCommentsForm();
},
toggleCommentsForm() {
this.setCommentsDisabled(this.currentValue === HISTORY_ONLY_FILTER_VALUE);
},
},
};
......@@ -73,6 +84,10 @@ export default {
>
{{ filter.title }}
</button>
<div
v-if="filter.value === defaultValue"
class="dropdown-divider"
></div>
</li>
</ul>
</div>
......
......@@ -191,7 +191,7 @@ export default {
:data-supports-quick-actions="!isEditing"
name="note[note]"
class="note-textarea js-gfm-input js-note-text
js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
js-autosize markdown-area js-vue-issue-note-form js-vue-textarea qa-reply-input"
aria-label="Description"
placeholder="Write a comment or drag your files here…"
@keydown.meta.enter="handleUpdate()"
......@@ -216,6 +216,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
<input
v-model="isUnresolving"
type="checkbox"
class="qa-unresolve-review-discussion"
/>
{{ __('Unresolve discussion') }}
</template>
......@@ -225,6 +226,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
<input
v-model="isResolving"
type="checkbox"
class="qa-resolve-review-discussion"
/>
{{ __('Resolve discussion') }}
</template>
......@@ -234,7 +236,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
<button
:disabled="isDisabled"
type="button"
class="btn btn-success"
class="btn btn-success qa-start-review"
@click="handleAddToReview()">
<template v-if="hasDrafts">
{{ __('Add to review') }}
......@@ -246,7 +248,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
<button
:disabled="isDisabled"
type="button"
class="btn"
class="btn qa-comment-now"
@click="handleUpdate()">
{{ __('Add comment now') }}
</button>
......
......@@ -385,7 +385,7 @@ Please check your network connection and try again.`;
role="group">
<button
type="button"
class="js-vue-discussion-reply btn btn-text-field mr-2"
class="js-vue-discussion-reply btn btn-text-field mr-2 qa-discussion-reply"
title="Add a reply"
@click="showReplyForm">Reply...</button>
</div>
......
......@@ -60,6 +60,7 @@ export default {
'getNotesDataByProp',
'discussionCount',
'isLoading',
'commentsDisabled',
]),
noteableType() {
return this.noteableData.noteableType;
......@@ -206,6 +207,7 @@ export default {
</ul>
<comment-form
v-if="!commentsDisabled"
:noteable-type="noteableType"
:markdown-version="markdownVersion"
/>
......
......@@ -15,6 +15,8 @@ export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest';
export const UNRESOLVE_NOTE_METHOD_NAME = 'delete';
export const RESOLVE_NOTE_METHOD_NAME = 'post';
export const DESCRIPTION_TYPE = 'changed the description';
export const HISTORY_ONLY_FILTER_VALUE = 2;
export const DISCUSSION_FILTERS_DEFAULT_VALUE = 0;
export const NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE,
......
......@@ -6,7 +6,7 @@ export default store => {
if (discussionFilterEl) {
const { defaultFilter, notesFilters } = discussionFilterEl.dataset;
const defaultValue = defaultFilter ? parseInt(defaultFilter, 10) : null;
const selectedValue = defaultFilter ? parseInt(defaultFilter, 10) : null;
const filterValues = notesFilters ? JSON.parse(notesFilters) : {};
const filters = Object.keys(filterValues).map(entry => ({
title: entry,
......@@ -24,7 +24,7 @@ export default store => {
return createElement('discussion-filter', {
props: {
filters,
defaultValue,
selectedValue,
},
});
},
......
......@@ -364,5 +364,9 @@ export const filterDiscussion = ({ dispatch }, { path, filter }) => {
});
};
export const setCommentsDisabled = ({ commit }, data) => {
commit(types.DISABLE_COMMENTS, data);
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
......@@ -195,5 +195,7 @@ export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => {
export const getDiscussion = state => discussionId =>
state.discussions.find(discussion => discussion.id === discussionId);
export const commentsDisabled = state => state.commentsDisabled;
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
......@@ -21,6 +21,7 @@ export default () => ({
noteableData: {
current_user: {},
},
commentsDisabled: false,
},
actions,
getters,
......
......@@ -15,6 +15,7 @@ export const UPDATE_DISCUSSION = 'UPDATE_DISCUSSION';
export const SET_DISCUSSION_DIFF_LINES = 'SET_DISCUSSION_DIFF_LINES';
export const SET_NOTES_FETCHED_STATE = 'SET_NOTES_FETCHED_STATE';
export const SET_NOTES_LOADING_STATE = 'SET_NOTES_LOADING_STATE';
export const DISABLE_COMMENTS = 'DISABLE_COMMENTS';
// DISCUSSION
export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION';
......
......@@ -225,4 +225,8 @@ export default {
discussion.truncated_diff_lines = diffLines;
},
[types.DISABLE_COMMENTS](state, value) {
state.commentsDisabled = value;
},
};
......@@ -29,7 +29,7 @@ export default {
if (action.scheduled_at) {
const confirmationMessage = sprintf(
s__(
"DelayedJobs|Are you sure you want to run %{jobName} immediately? This job will run automatically after it's timer finishes.",
"DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes.",
),
{ jobName: action.name },
);
......
......@@ -5,7 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import GfmAutoComplete from '~/gfm_auto_complete';
import { __, s__ } from '~/locale';
import Api from '~/api';
import { GlModal } from '@gitlab-org/gitlab-ui';
import { GlModal, GlTooltipDirective } from '@gitlab-org/gitlab-ui';
import eventHub from './event_hub';
import EmojiMenuInModal from './emoji_menu_in_modal';
......@@ -16,6 +16,9 @@ export default {
Icon,
GlModal,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
currentEmoji: {
type: String,
......
<script>
import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility';
import { GlTooltipDirective } from '@gitlab-org/gitlab-ui';
/**
* Counts down to a given end date.
*/
export default {
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
endDateString: {
type: String,
......
......@@ -348,6 +348,7 @@
@include media-breakpoint-down(xs) {
width: 100%;
margin: $btn-side-margin 0;
}
}
}
......
......@@ -322,15 +322,15 @@
width: $contextual-sidebar-width - 1px;
transition: width $sidebar-transition-duration;
position: fixed;
height: $toggle-sidebar-height;
bottom: 0;
padding: $gl-padding;
padding: 0 $gl-padding;
background-color: $gray-light;
border: 0;
border-top: 1px solid $border-color;
color: $gl-text-color-secondary;
display: flex;
align-items: center;
line-height: 1;
svg {
margin-right: 8px;
......
......@@ -291,7 +291,7 @@
/*
* Mixin that handles the position of the controls placed on the top bar
*/
@mixin build-controllers($control-font-size, $flex-direction, $with-grow, $flex-grow-size) {
@mixin build-controllers($control-font-size, $flex-direction, $with-grow, $flex-grow-size, $svg-display: 'block', $svg-top: '2px') {
display: flex;
font-size: $control-font-size;
justify-content: $flex-direction;
......@@ -304,8 +304,9 @@
svg {
width: 15px;
height: 15px;
display: block;
display: $svg-display;
fill: $gl-text-color;
top: $svg-top;
}
.controllers-buttons {
......
......@@ -147,3 +147,9 @@ table {
}
}
}
.top-area + .content-list {
th {
border-top: 0;
}
}
......@@ -10,6 +10,7 @@ $sidebar-breakpoint: 1024px;
$default-transition-duration: 0.15s;
$contextual-sidebar-width: 220px;
$contextual-sidebar-collapsed-width: 50px;
$toggle-sidebar-height: 48px;
/*
* Color schema
......
......@@ -57,10 +57,6 @@
.top-bar {
@include build-trace-top-bar(35px);
&.sidebar-expanded {
margin-right: calc(#{$gutter-width} - 16px);
}
.truncated-info {
.truncated-info-size {
margin: 0 5px;
......@@ -74,7 +70,7 @@
}
.controllers {
@include build-controllers(15px, center, false, 0);
@include build-controllers(15px, center, false, 0, inline, 0);
}
}
......
......@@ -44,11 +44,6 @@
margin: 0;
}
.icon-play {
height: 13px;
width: 12px;
}
.external-url,
.dropdown-new {
color: $gl-text-color-secondary;
......@@ -526,7 +521,7 @@
}
.arrow-shadow {
content: "";
content: '';
position: absolute;
width: 7px;
height: 7px;
......
......@@ -39,10 +39,6 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
svg {
vertical-align: middle;
}
}
.next-run-cell {
......@@ -52,6 +48,10 @@
a {
color: $text-color;
}
svg {
vertical-align: middle;
}
}
.pipeline-schedules-user-callout {
......
......@@ -3,7 +3,7 @@
class PersonalAccessTokensFinder
attr_accessor :params
delegate :build, :find, :find_by, :find_by_token, to: :execute
delegate :build, :find, :find_by_id, :find_by_token, to: :execute
def initialize(params = {})
@params = params
......
......@@ -116,6 +116,7 @@ module ApplicationSettingsHelper
:akismet_api_key,
:akismet_enabled,
:allow_local_requests_from_hooks_and_services,
:archive_builds_in_human_readable,
:authorized_keys_enabled,
:auto_devops_enabled,
:auto_devops_domain,
......
......@@ -109,6 +109,8 @@ module IconsHelper
def file_type_icon_class(type, mode, name)
if type == 'folder'
icon_class = 'folder'
elsif type == 'archive'
icon_class = 'archive'
elsif mode == '120000'
icon_class = 'share'
else
......
......@@ -31,11 +31,21 @@ module TreeHelper
# mode - File unix mode
# name - File name
def tree_icon(type, mode, name)
icon("#{file_type_icon_class(type, mode, name)} fw")
icon([file_type_icon_class(type, mode, name), 'fw'])
end
def tree_hex_class(content)
"file_#{hexdigest(content.name)}"
# Using Rails `*_path` methods can be slow, especially when generating
# many paths, as with a repository tree that has thousands of items.
def fast_project_blob_path(project, blob_path)
Addressable::URI.escape(
File.join(relative_url_root, project.path_with_namespace, 'blob', blob_path)
)
end
def fast_project_tree_path(project, tree_path)
Addressable::URI.escape(
File.join(relative_url_root, project.path_with_namespace, 'tree', tree_path)
)
end
# Simple shortcut to File.join
......@@ -142,4 +152,8 @@ module TreeHelper
def selected_branch
@branch_name || tree_edit_branch
end
def relative_url_root
Gitlab.config.gitlab.relative_url_root.presence || '/'
end
end
......@@ -5,6 +5,8 @@ class ApplicationSetting < ActiveRecord::Base
include CacheMarkdownField
include TokenAuthenticatable
include IgnorableColumn
include ChronicDurationAttribute
prepend EE::ApplicationSetting
add_authentication_token_field :runners_registration_token
......@@ -46,6 +48,8 @@ class ApplicationSetting < ActiveRecord::Base
default_value_for :id, 1
chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds
validates :uuid, presence: true
validates :session_expire_delay,
......@@ -185,6 +189,10 @@ class ApplicationSetting < ActiveRecord::Base
validates :user_default_internal_regex, js_regex: true, allow_nil: true
validates :archive_builds_in_seconds,
allow_nil: true,
numericality: { only_integer: true, greater_than_or_equal_to: 1.day.seconds }
SUPPORTED_KEY_TYPES.each do |type|
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
end
......@@ -442,6 +450,10 @@ class ApplicationSetting < ActiveRecord::Base
latest_terms
end
def archive_builds_older_than
archive_builds_in_seconds.seconds.ago if archive_builds_in_seconds
end
private
def ensure_uuid!
......
......@@ -249,17 +249,37 @@ module Ci
.fabricate!
end
def other_actions
def other_manual_actions
pipeline.manual_actions.where.not(name: name)
end
def other_scheduled_actions
pipeline.scheduled_actions.where.not(name: name)
end
def pages_generator?
Gitlab.config.pages.enabled &&
self.name == 'pages'
end
# degenerated build is one that cannot be run by Runner
def degenerated?
self.options.nil?
end
def degenerate!
self.update!(options: nil, yaml_variables: nil, commands: nil)
end
def archived?
return true if degenerated?
archive_builds_older_than = Gitlab::CurrentSettings.current_application_settings.archive_builds_older_than
archive_builds_older_than.present? && created_at < archive_builds_older_than
end
def playable?
action? && (manual? || scheduled? || retryable?)
action? && !archived? && (manual? || scheduled? || retryable?)
end
def schedulable?
......@@ -287,7 +307,7 @@ module Ci
end
def retryable?
success? || failed? || canceled?
!archived? && (success? || failed? || canceled?)
end
def retries_count
......@@ -295,7 +315,7 @@ module Ci
end
def retries_max
self.options.fetch(:retry, 0).to_i
self.options.to_h.fetch(:retry, 0).to_i
end
def latest?
......
......@@ -17,7 +17,7 @@ module Ci
metadata: nil,
trace: nil,
junit: 'junit.xml',
codequality: 'codequality.json',
codequality: 'gl-code-quality-report.json',
sast: 'gl-sast-report.json',
dependency_scanning: 'gl-dependency-scanning-report.json',
container_scanning: 'gl-container-scanning-report.json',
......
......@@ -53,7 +53,8 @@ class CommitStatus < ActiveRecord::Base
missing_dependency_failure: 5,
runner_unsupported: 6,
stale_schedule: 7,
job_execution_timeout: 8
job_execution_timeout: 8,
archived_failure: 9
}.merge(EE_FAILURE_REASONS)
##
......@@ -169,16 +170,18 @@ class CommitStatus < ActiveRecord::Base
false
end
# To be overridden when inherrited from
def retryable?
false
end
# To be overridden when inherrited from
def cancelable?
false
end
def archived?
false
end
def stuck?
false
end
......
......@@ -4,6 +4,7 @@ class DeployToken < ActiveRecord::Base
include Expirable
include TokenAuthenticatable
include PolicyActor
include Gitlab::Utils::StrongMemoize
add_authentication_token_field :token
AVAILABLE_SCOPES = %i(read_repository read_registry).freeze
......@@ -49,8 +50,10 @@ class DeployToken < ActiveRecord::Base
# to a single project, later we're going to extend
# that to be for multiple projects and namespaces.
def project
strong_memoize(:project) do
projects.first
end
end
def expires_at
expires_at = read_attribute(:expires_at)
......
......@@ -55,7 +55,11 @@ class Deployment < ActiveRecord::Base
end
def manual_actions
@manual_actions ||= deployable.try(:other_actions)
@manual_actions ||= deployable.try(:other_manual_actions)
end
def scheduled_actions
@scheduled_actions ||= deployable.try(:other_scheduled_actions)
end
def includes_commit?(commit)
......
......@@ -119,6 +119,8 @@ class Note < ActiveRecord::Base
case notes_filter
when UserPreference::NOTES_FILTERS[:only_comments]
user
when UserPreference::NOTES_FILTERS[:only_activity]
system
else
all
end
......
# frozen_string_literal: true
class PoolRepository < ActiveRecord::Base
POOL_PREFIX = '@pools'
belongs_to :shard
validates :shard, presence: true
# For now, only pool repositories are tracked in the database. However, we may
# want to add other repository types in the future
self.table_name = 'repositories'
has_many :pool_member_projects, class_name: 'Project', foreign_key: :pool_repository_id
def shard_name
shard&.name
end
def shard_name=(name)
self.shard = Shard.by_name(name)
end
end
......@@ -4,6 +4,15 @@ module Postgresql
class ReplicationSlot < ActiveRecord::Base
self.table_name = 'pg_replication_slots'
# Returns true if there are any replication slots in use.
# PostgreSQL-compatible databases such as Aurora don't support
# replication slots, so this will return false as well.
def self.in_use?
transaction { exists? }
rescue ActiveRecord::StatementInvalid
false
end
# Returns true if the lag observed across all replication slots exceeds a
# given threshold.
#
......@@ -11,6 +20,8 @@ module Postgresql
# statistics it takes between 1 and 5 seconds to replicate around
# 100 MB of data.
def self.lag_too_great?(max = 100.megabytes)
return false unless in_use?
lag_function = "#{Gitlab::Database.pg_wal_lsn_diff}" \
"(#{Gitlab::Database.pg_current_wal_insert_lsn}(), restart_lsn)::bigint"
......
......@@ -98,8 +98,7 @@ class Project < ActiveRecord::Base
unless: :ci_cd_settings,
if: proc { ProjectCiCdSetting.available? }
after_create :set_last_activity_at
after_create :set_last_repository_updated_at
after_create :set_timestamps_for_create
after_update :update_forks_visibility_level
before_destroy :remove_private_deploy_keys
......@@ -127,6 +126,7 @@ class Project < ActiveRecord::Base
alias_attribute :title, :name
# Relations
belongs_to :pool_repository
belongs_to :creator, class_name: 'User'
belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id'
belongs_to :namespace
......@@ -2101,13 +2101,8 @@ class Project < ActiveRecord::Base
gitlab_shell.exists?(repository_storage, "#{disk_path}.git")
end
# set last_activity_at to the same as created_at
def set_last_activity_at
update_column(:last_activity_at, self.created_at)
end
def set_last_repository_updated_at
update_column(:last_repository_updated_at, self.created_at)
def set_timestamps_for_create
update_columns(last_activity_at: self.created_at, last_repository_updated_at: self.created_at)
end
def cross_namespace_reference?(from)
......
# frozen_string_literal: true
class Shard < ActiveRecord::Base
# Store shard names from the configuration file in the database. This is not a
# list of active shards - we just want to assign an immutable, unique ID to
# every shard name for easy indexing / referencing.
def self.populate!
return unless table_exists?
# The GitLab config does not change for the lifecycle of the process
in_config = Gitlab.config.repositories.storages.keys.map(&:to_s)
transaction do
in_db = all.pluck(:name)
missing = in_config - in_db
missing.map { |name| by_name(name) }
end
end
def self.by_name(name)
find_or_create_by(name: name)
rescue ActiveRecord::RecordNotUnique
retry
end
end
......@@ -460,12 +460,6 @@ class User < ActiveRecord::Base
by_username(username).take!
end
def find_by_personal_access_token(token_string)
return unless token_string
PersonalAccessTokensFinder.new(state: 'active').find_by_token(token_string)&.user # rubocop: disable CodeReuse/Finder
end
# Returns a user for the given SSH key.
def find_by_ssh_key_id(key_id)
Key.find_by(id: key_id)&.user
......
......@@ -4,7 +4,7 @@ class UserPreference < ActiveRecord::Base
# We could use enums, but Rails 4 doesn't support multiple
# enum options with same name for multiple fields, also it creates
# extra methods that aren't really needed here.
NOTES_FILTERS = { all_notes: 0, only_comments: 1 }.freeze
NOTES_FILTERS = { all_notes: 0, only_comments: 1, only_activity: 2 }.freeze
belongs_to :user
......@@ -14,7 +14,8 @@ class UserPreference < ActiveRecord::Base
def notes_filters
{
s_('Notes|Show all activity') => NOTES_FILTERS[:all_notes],
s_('Notes|Show comments only') => NOTES_FILTERS[:only_comments]
s_('Notes|Show comments only') => NOTES_FILTERS[:only_comments],
s_('Notes|Show history only') => NOTES_FILTERS[:only_activity]
}
end
end
......
......@@ -22,12 +22,17 @@ module Ci
@subject.project.branch_allows_collaboration?(@user, @subject.ref)
end
condition(:archived, scope: :subject) do
@subject.archived?
end
condition(:terminal, scope: :subject) do
@subject.has_terminal?
end
rule { protected_ref }.policy do
rule { protected_ref | archived }.policy do
prevent :update_build
prevent :update_commit_status
prevent :erase_build
end
......
......@@ -2,4 +2,13 @@
class DeploymentPolicy < BasePolicy
delegate { @subject.project }
condition(:can_retry_deployable) do
can?(:update_build, @subject.deployable)
end
rule { ~can_retry_deployable }.policy do
prevent :create_deployment
prevent :update_deployment
end
end
......@@ -9,7 +9,8 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
missing_dependency_failure: 'There has been a missing dependency failure',
runner_unsupported: 'Your runner is outdated, please upgrade your runner',
stale_schedule: 'Delayed job could not be executed by some reason, please try again',
job_execution_timeout: 'The script exceeded the maximum execution time set for the job'
job_execution_timeout: 'The script exceeded the maximum execution time set for the job',
archived_failure: 'The job is archived and cannot be run'
}.freeze
private_constant :CALLOUT_FAILURE_MESSAGES
......@@ -31,6 +32,6 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
end
def unrecoverable?
script_failure? || missing_dependency_failure?
script_failure? || missing_dependency_failure? || archived_failure?
end
end
......@@ -25,4 +25,5 @@ class DeploymentEntity < Grape::Entity
expose :commit, using: CommitEntity
expose :deployable, using: JobEntity
expose :manual_actions, using: JobEntity
expose :scheduled_actions, using: JobEntity
end
......@@ -7,6 +7,7 @@ class JobEntity < Grape::Entity
expose :name
expose :started?, as: :started
expose :archived?, as: :archived
expose :build_path do |build|
build_path(build)
......
......@@ -7,4 +7,8 @@ class UserPreferenceEntity < Grape::Entity
expose :notes_filters do |user_preference|
UserPreference.notes_filters
end
expose :default_notes_filter do |user_preference|
UserPreference::NOTES_FILTERS[:all_notes]
end
end
......@@ -84,6 +84,11 @@ module Ci
return false
end
if build.archived?
build.drop!(:archived_failure)
return false
end
build.run!
true
end
......
......@@ -44,5 +44,13 @@
The default unit is in seconds, but you can define an alternative. For example:
<code>4 mins 2 sec</code>, <code>2h42min</code>.
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
.form-group
= f.label :archive_builds_in_human_readable, 'Archive builds in', class: 'label-bold'
= f.text_field :archive_builds_in_human_readable, class: 'form-control', placeholder: 'never'
.form-text.text-muted
Set the duration when build gonna be considered old. Archived builds cannot be retried.
Make it empty to never expire builds. It has to be larger than 1 day.
The default unit is in seconds, but you can define an alternative. For example:
<code>4 mins 2 sec</code>, <code>2h42min</code>.
= f.submit 'Save changes', class: "btn btn-success"
......@@ -5,7 +5,7 @@
- subscribed = params[:subscribed]
- labels_or_filters = @labels.exists? || search.present? || subscribed.present?
- if can_admin_label
- if @labels.present? && can_admin_label
- content_for(:header_content) do
.nav-controls
= link_to _('New label'), new_group_label_path(@group), class: "btn btn-success"
......
- if can?(current_user, :create_deployment, deployment) && deployment.deployable
- if can?(current_user, :create_deployment, deployment)
- tooltip = deployment.last? ? s_('Environments|Re-deploy to environment') : s_('Environments|Rollback environment')
= link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build has-tooltip', title: tooltip do
- if deployment.last?
......
......@@ -5,7 +5,7 @@
- subscribed = params[:subscribed]
- labels_or_filters = @labels.exists? || @prioritized_labels.exists? || search.present? || subscribed.present?
- if can_admin_label
- if @labels.present? && can_admin_label
- content_for(:header_content) do
.nav-controls
= link_to _('New label'), new_project_label_path(@project), class: "btn btn-success qa-label-create-new"
......
......@@ -34,7 +34,7 @@
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
%ul.merge-request-tabs.nav-tabs.nav.nav-links.scrolling-tabs
%li.notes-tab
%li.notes-tab.qa-notes-tab
= tab_link_for @merge_request, :show, force_link: @commit.present? do
Discussion
%span.badge.badge-pill= @merge_request.related_notes.user.count
......@@ -48,7 +48,7 @@
= tab_link_for @merge_request, :pipelines do
Pipelines
%span.badge.badge-pill.js-pipelines-mr-count= @pipelines.size
%li.diffs-tab
%li.diffs-tab.qa-diffs-tab
= tab_link_for @merge_request, :diffs do
Changes
%span.badge.badge-pill= @merge_request.diff_size
......
......@@ -61,8 +61,10 @@
%td.responsive-table-cell.build-failure{ data: { column: _("Failure")} }
= build.present.callout_failure_message
%td.responsive-table-cell.build-actions
- if can?(current_user, :update_build, job)
= link_to retry_project_job_path(build.project, build, return_to: request.original_url), method: :post, title: _('Retry'), class: 'btn btn-build' do
= icon('repeat')
- if can?(current_user, :read_build, job)
%tr.build-trace-row.responsive-table-border-end
%td
%td.responsive-table-cell.build-trace-container{ colspan: 4 }
......
- is_lfs_blob = @lfs_blob_ids.include?(blob_item.id)
%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
%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)), class: 'str-truncated', title: file_name do
%span= file_name
- if is_lfs_blob
%span.badge.label-lfs.prepend-left-5 LFS
%td.d-none.d-sm-table-cell.tree-commit
%td.tree-time-ago.cgray.text-right
= render 'projects/tree/spinner'
%span.log_loading.hide
%i.fa.fa-spinner.fa-spin
Loading commit data...
%tr.tree-item
%td.tree-item-file-name
%i.fa.fa-archive.fa-fw
= submodule_link(submodule_item, @ref)
%td
%td.d-none.d-sm-table-cell
%tr{ class: "tree-item #{tree_hex_class(tree_item)}" }
%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)), class: 'str-truncated', title: path do
%span= path
%td.d-none.d-sm-table-cell.tree-commit
%td.tree-time-ago.text-right
= render 'projects/tree/spinner'
- if tree_row.type == :tree
= render partial: 'projects/tree/tree_item', object: tree_row, as: 'tree_item', locals: { type: 'folder' }
- elsif tree_row.type == :blob
= render partial: 'projects/tree/blob_item', object: tree_row, as: 'blob_item', locals: { type: 'file' }
- elsif tree_row.type == :commit
= render partial: 'projects/tree/submodule_item', object: tree_row, as: 'submodule_item'
- tree_row_name = tree_row.name
- tree_row_type = tree_row.type
%tr{ class: "tree-item file_#{hexdigest(tree_row_name)}" }
%td.tree-item-file-name
- if tree_row_type == :tree
= tree_icon('folder', tree_row.mode, tree_row.name)
- path = flatten_tree(@path, tree_row)
%a.str-truncated{ href: fast_project_tree_path(@project, tree_join(@id || @commit.id, path)), title: path }
%span= path
- elsif tree_row_type == :blob
= tree_icon('file', tree_row.mode, tree_row_name)
%a.str-truncated{ href: fast_project_blob_path(@project, tree_join(@id || @commit.id, tree_row_name)), title: tree_row_name }
%span= tree_row_name
- if @lfs_blob_ids.include?(tree_row.id)
%span.badge.label-lfs.prepend-left-5 LFS
- elsif tree_row_type == :commit
= tree_icon('archive', tree_row.mode, tree_row.name)
= submodule_link(tree_row, @ref)
%td.d-none.d-sm-table-cell.tree-commit
%td.tree-time-ago.text-right
%span.log_loading.hide
%i.fa.fa-spinner.fa-spin
Loading commit data...
......@@ -6,6 +6,9 @@
.text-content
%h4= _("Labels can be applied to issues and merge requests to categorize them.")
%p= _("You can also star a label to make it a priority label.")
.text-center
- if can?(current_user, :admin_label, @project)
= link_to _('New label'), new_project_label_path(@project), class: 'btn btn-success', title: _('New label'), id: 'new_label_link'
= link_to _('Generate a default set of labels'), generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: _('Generate a default set of labels'), id: 'generate_labels_link'
- if can?(current_user, :admin_label, @group)
= link_to _('New label'), new_group_label_path(@group), class: 'btn btn-success', title: _('New label'), id: 'new_label_link'
---
title: Uses gitlab-ui components in jobs components
merge_request:
author:
type: other
---
title: Start tracking shards and pool repositories in the database
merge_request: 22482
author:
type: other
---
title: Fixed merge request fill tree toggling not respecting fluid width preference
title: Fixing styling issues on the scheduled pipelines page
merge_request:
author:
type: fixed
---
title: "fix link to enable usage ping from convdev index"
merge_request: 22545
author: Anand Capur
type: fixed
---
title: Remove PersonalAccessTokensFinder#find_by method
merge_request: 22617
author:
type: fixed
---
title: Allow Rails concurrency when running in Puma
merge_request: 22751
author:
type: performance
---
title: Bump KUBERNETES_VERSION for Auto DevOps to latest 1.10 series
merge_request: 22757
author:
type: other
---
title: Soft-archive old jobs
merge_request:
author:
type: added
---
title: Improve performance of tree rendering in repositories with lots of items
merge_request:
author:
type: performance
---
title: Enable frozen string for remaining lib/gitlab/ci/**/*.rb
merge_request:
author: gfyoung
type: performance
---
title: Add indexes to all geo event foreign keys
merge_request: 7990
title: Remove gitlab-ui's tooltip from global
merge_request:
author:
type: performance
---
title: Update project and group labels empty state
merge_request: 22745
author: George Tsiolis
type: changed
---
title: Add 'only history' option to notes filter
merge_request:
author:
type: changed
---
title: Add the Play button for delayed jobs in environment page
merge_request: 22106
author:
type: added
---
title: Disable replication lag check for Aurora PostgreSQL databases
merge_request: 22786
author:
type: fixed
---
title: Align toggle sidebar button across all browsers and OSs
merge_request: 22771
author:
type: fixed
---
title: "Remove dind from license_management auto-devops job definition"
merge_request: 22732
author:
type: performance
---
title: Bump Gitaly to 0.128.0
merge_request:
author:
type: added
......@@ -45,4 +45,6 @@ Rails.application.configure do
# Do not log asset requests
config.assets.quiet = true
config.allow_concurrency = defined?(::Puma)
end
......@@ -83,5 +83,5 @@ Rails.application.configure do
config.eager_load = true
config.allow_concurrency = false
config.allow_concurrency = defined?(::Puma)
end
return unless Shard.connected?
return if Gitlab::Database.read_only?
Shard.populate!
# frozen_string_literal: true
class AddIndexToProjectDeployTokensDeployTokenId < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
# MySQL already has index inserted
add_concurrent_index :project_deploy_tokens, :deploy_token_id if Gitlab::Database.postgresql?
end
def down
remove_concurrent_index(:project_deploy_tokens, :deploy_token_id) if Gitlab::Database.postgresql?
end
end
# frozen_string_literal: true
class AddShardsTable < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :shards do |t|
t.string :name, null: false, index: { unique: true }
end
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment