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. 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) ## 11.4.4 (2018-10-30)
- No changes. - No changes.
......
...@@ -2,6 +2,21 @@ ...@@ -2,6 +2,21 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. 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) ## 11.4.4 (2018-10-30)
### Security (1 change) ### Security (1 change)
......
import Vue from 'vue'; 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.component('gl-loading-icon', GlLoadingIcon);
Vue.directive('gl-tooltip', GlTooltipDirective);
...@@ -3,6 +3,7 @@ import { mapActions } from 'vuex'; ...@@ -3,6 +3,7 @@ import { mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { pluralize, truncate } from '~/lib/utils/text_utility'; import { pluralize, truncate } from '~/lib/utils/text_utility';
import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue'; 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'; import { COUNT_OF_AVATARS_IN_GUTTER, LENGTH_OF_AVATAR_TOOLTIP } from '../constants';
export default { export default {
...@@ -10,6 +11,9 @@ export default { ...@@ -10,6 +11,9 @@ export default {
Icon, Icon,
UserAvatarImage, UserAvatarImage,
}, },
directives: {
GlTooltip: GlTooltipDirective,
},
props: { props: {
discussions: { discussions: {
type: Array, type: Array,
......
...@@ -167,7 +167,7 @@ export default { ...@@ -167,7 +167,7 @@ export default {
<button <button
v-if="shouldShowCommentButton" v-if="shouldShowCommentButton"
type="button" 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" title="Add a comment to this line"
@click="handleCommentButton" @click="handleCommentButton"
> >
......
...@@ -102,7 +102,7 @@ export default { ...@@ -102,7 +102,7 @@ export default {
:line-type="newLineType" :line-type="newLineType"
:is-bottom="isBottom" :is-bottom="isBottom"
:is-hover="isHover" :is-hover="isHover"
class="diff-line-num new_line" class="diff-line-num new_line qa-new-diff-line"
/> />
<td <td
:class="line.type" :class="line.type"
......
<script> <script>
import { s__, sprintf } from '~/locale';
import { formatTime } from '~/lib/utils/datetime_utility';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
...@@ -28,10 +30,24 @@ export default { ...@@ -28,10 +30,24 @@ export default {
}, },
}, },
methods: { 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; this.isLoading = true;
eventHub.$emit('postAction', { endpoint }); eventHub.$emit('postAction', { endpoint: action.playPath });
}, },
isActionDisabled(action) { isActionDisabled(action) {
...@@ -41,6 +57,11 @@ export default { ...@@ -41,6 +57,11 @@ export default {
return !action.playable; return !action.playable;
}, },
remainingTime(action) {
const remainingMilliseconds = new Date(action.scheduledAt).getTime() - Date.now();
return formatTime(Math.max(0, remainingMilliseconds));
},
}, },
}; };
</script> </script>
...@@ -54,7 +75,7 @@ export default { ...@@ -54,7 +75,7 @@ export default {
:aria-label="title" :aria-label="title"
:disabled="isLoading" :disabled="isLoading"
type="button" 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-container="body"
data-toggle="dropdown" data-toggle="dropdown"
> >
...@@ -75,12 +96,19 @@ export default { ...@@ -75,12 +96,19 @@ export default {
:class="{ disabled: isActionDisabled(action) }" :class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)" :disabled="isActionDisabled(action)"
type="button" type="button"
class="js-manual-action-link no-btn btn" class="js-manual-action-link no-btn btn d-flex align-items-center"
@click="onClickAction(action.play_path)" @click="onClickAction(action)"
> >
<span> <span class="flex-fill">
{{ action.name }} {{ action.name }}
</span> </span>
<span
v-if="action.scheduledAt"
class="text-secondary"
>
<icon name="clock" />
{{ remainingTime(action) }}
</span>
</button> </button>
</li> </li>
</ul> </ul>
......
...@@ -13,6 +13,7 @@ import TerminalButtonComponent from './environment_terminal_button.vue'; ...@@ -13,6 +13,7 @@ import TerminalButtonComponent from './environment_terminal_button.vue';
import MonitoringButtonComponent from './environment_monitoring.vue'; import MonitoringButtonComponent from './environment_monitoring.vue';
import CommitComponent from '../../vue_shared/components/commit.vue'; import CommitComponent from '../../vue_shared/components/commit.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
/** /**
* Environment Item Component * Environment Item Component
...@@ -73,21 +74,6 @@ export default { ...@@ -73,21 +74,6 @@ export default {
return false; 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. * Checkes whether the environment is protected.
* (`is_protected` currently only set in EE) * (`is_protected` currently only set in EE)
...@@ -154,23 +140,20 @@ export default { ...@@ -154,23 +140,20 @@ export default {
return ''; return '';
}, },
/** actions() {
* Returns the manual actions with the name parsed. if (!this.model || !this.model.last_deployment || !this.canCreateDeployment) {
* return [];
* @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;
});
} }
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 { ...@@ -443,7 +426,7 @@ export default {
displayEnvironmentActions() { displayEnvironmentActions() {
return ( return (
this.hasManualActions || this.actions.length > 0 ||
this.externalURL || this.externalURL ||
this.monitoringUrl || this.monitoringUrl ||
this.canStopEnvironment || this.canStopEnvironment ||
...@@ -641,8 +624,8 @@ export default { ...@@ -641,8 +624,8 @@ export default {
/> />
<actions-component <actions-component
v-if="hasManualActions && canCreateDeployment" v-if="actions.length > 0"
:actions="manualActions" :actions="actions"
/> />
<terminal-button-component <terminal-button-component
......
<script> <script>
import { GlLink } from '@gitlab-org/gitlab-ui';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago'; import timeagoMixin from '~/vue_shared/mixins/timeago';
export default { export default {
components: { components: {
TimeagoTooltip, TimeagoTooltip,
GlLink,
}, },
mixins: [timeagoMixin], mixins: [timeagoMixin],
props: { props: {
...@@ -53,16 +55,16 @@ export default { ...@@ -53,16 +55,16 @@ export default {
class="btn-group d-flex" class="btn-group d-flex"
role="group" role="group"
> >
<a <gl-link
v-if="artifact.keep_path" v-if="artifact.keep_path"
:href="artifact.keep_path" :href="artifact.keep_path"
class="js-keep-artifacts btn btn-sm btn-default" class="js-keep-artifacts btn btn-sm btn-default"
data-method="post" data-method="post"
> >
{{ s__('Job|Keep') }} {{ s__('Job|Keep') }}
</a> </gl-link>
<a <gl-link
v-if="artifact.download_path" v-if="artifact.download_path"
:href="artifact.download_path" :href="artifact.download_path"
class="js-download-artifacts btn btn-sm btn-default" class="js-download-artifacts btn btn-sm btn-default"
...@@ -70,15 +72,15 @@ export default { ...@@ -70,15 +72,15 @@ export default {
rel="nofollow" rel="nofollow"
> >
{{ s__('Job|Download') }} {{ s__('Job|Download') }}
</a> </gl-link>
<a <gl-link
v-if="artifact.browse_path" v-if="artifact.browse_path"
:href="artifact.browse_path" :href="artifact.browse_path"
class="js-browse-artifacts btn btn-sm btn-default" class="js-browse-artifacts btn btn-sm btn-default"
> >
{{ s__('Job|Browse') }} {{ s__('Job|Browse') }}
</a> </gl-link>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { GlLink } from '@gitlab-org/gitlab-ui';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
export default { export default {
components: { components: {
ClipboardButton, ClipboardButton,
GlLink,
}, },
props: { props: {
commit: { commit: {
...@@ -31,10 +33,10 @@ export default { ...@@ -31,10 +33,10 @@ export default {
<p> <p>
{{ __('Commit') }} {{ __('Commit') }}
<a <gl-link
:href="commit.commit_path" :href="commit.commit_path"
class="js-commit-sha commit-sha link-commit" class="js-commit-sha commit-sha link-commit"
>{{ commit.short_id }}</a> >{{ commit.short_id }}</gl-link>
<clipboard-button <clipboard-button
:text="commit.short_id" :text="commit.short_id"
...@@ -42,11 +44,11 @@ export default { ...@@ -42,11 +44,11 @@ export default {
css-class="btn btn-clipboard btn-transparent" css-class="btn btn-clipboard btn-transparent"
/> />
<a <gl-link
v-if="mergeRequest" v-if="mergeRequest"
:href="mergeRequest.path" :href="mergeRequest.path"
class="js-link-commit link-commit" class="js-link-commit link-commit"
>!{{ mergeRequest.iid }}</a> >!{{ mergeRequest.iid }}</gl-link>
</p> </p>
<p class="build-light-text append-bottom-0"> <p class="build-light-text append-bottom-0">
......
<script> <script>
import { GlLink } from '@gitlab-org/gitlab-ui';
export default { export default {
components: {
GlLink,
},
props: { props: {
illustrationPath: { illustrationPath: {
type: String, type: String,
...@@ -62,13 +67,13 @@ export default { ...@@ -62,13 +67,13 @@ export default {
v-if="action" v-if="action"
class="text-center" class="text-center"
> >
<a <gl-link
:href="action.path" :href="action.path"
:data-method="action.method" :data-method="action.method"
class="js-job-empty-state-action btn btn-primary" class="js-job-empty-state-action btn btn-primary"
> >
{{ action.button_title }} {{ action.button_title }}
</a> </gl-link>
</div> </div>
</div> </div>
</div> </div>
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { GlLink } from '@gitlab-org/gitlab-ui';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default { export default {
components: { components: {
TimeagoTooltip, TimeagoTooltip,
GlLink,
}, },
props: { props: {
user: { user: {
...@@ -29,9 +31,9 @@ export default { ...@@ -29,9 +31,9 @@ export default {
<div class="erased alert alert-warning"> <div class="erased alert alert-warning">
<template v-if="isErasedByUser"> <template v-if="isErasedByUser">
{{ s__("Job|Job has been erased by") }} {{ s__("Job|Job has been erased by") }}
<a :href="user.web_url"> <gl-link :href="user.web_url">
{{ user.username }} {{ user.username }}
</a> </gl-link>
</template> </template>
<template v-else> <template v-else>
{{ s__("Job|Job has been erased") }} {{ s__("Job|Job has been erased") }}
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { mapGetters, mapState, mapActions } from 'vuex'; import { mapGetters, mapState, mapActions } from 'vuex';
import { GlLoadingIcon } from '@gitlab-org/gitlab-ui';
import { isScrolledToBottom } from '~/lib/utils/scroll_utils'; import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
import bp from '~/breakpoints'; import bp from '~/breakpoints';
import CiHeader from '~/vue_shared/components/header_ci_component.vue'; import CiHeader from '~/vue_shared/components/header_ci_component.vue';
...@@ -26,6 +27,7 @@ export default { ...@@ -26,6 +27,7 @@ export default {
EmptyState, EmptyState,
EnvironmentsBlock, EnvironmentsBlock,
ErasedBlock, ErasedBlock,
GlLoadingIcon,
Log, Log,
LogTopBar, LogTopBar,
StuckBlock, StuckBlock,
......
<script> <script>
import { GlTooltipDirective, GlLink } from '@gitlab-org/gitlab-ui';
import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
export default { export default {
components: { components: {
CiIcon, CiIcon,
Icon, Icon,
GlLink,
}, },
directives: { directives: {
tooltip, GlTooltip: GlTooltipDirective,
}, },
props: { props: {
job: { job: {
...@@ -37,11 +38,10 @@ export default { ...@@ -37,11 +38,10 @@ export default {
active: isActive active: isActive
}" }"
> >
<a <gl-link
v-tooltip v-gl-tooltip
:href="job.status.details_path" :href="job.status.details_path"
:title="tooltipText" :title="tooltipText"
data-container="body"
data-boundary="viewport" data-boundary="viewport"
class="js-job-link" class="js-job-link"
> >
...@@ -60,6 +60,6 @@ export default { ...@@ -60,6 +60,6 @@ export default {
name="retry" name="retry"
class="js-retry-icon" class="js-retry-icon"
/> />
</a> </gl-link>
</div> </div>
</template> </template>
<script> <script>
import { GlTooltipDirective, GlLink, GlButton } from '@gitlab-org/gitlab-ui';
import { polyfillSticky } from '~/lib/utils/sticky'; import { polyfillSticky } from '~/lib/utils/sticky';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import { numberToHumanSize } from '~/lib/utils/number_utils'; import { numberToHumanSize } from '~/lib/utils/number_utils';
import { sprintf } from '~/locale'; import { sprintf } from '~/locale';
import scrollDown from '../svg/scroll_down.svg'; import scrollDown from '../svg/scroll_down.svg';
...@@ -9,9 +9,11 @@ import scrollDown from '../svg/scroll_down.svg'; ...@@ -9,9 +9,11 @@ import scrollDown from '../svg/scroll_down.svg';
export default { export default {
components: { components: {
Icon, Icon,
GlLink,
GlButton,
}, },
directives: { directives: {
tooltip, GlTooltip: GlTooltipDirective,
}, },
scrollDown, scrollDown,
props: { props: {
...@@ -73,76 +75,70 @@ export default { ...@@ -73,76 +75,70 @@ export default {
<template v-if="isTraceSizeVisible"> <template v-if="isTraceSizeVisible">
{{ jobLogSize }} {{ jobLogSize }}
<a <gl-link
v-if="rawPath" v-if="rawPath"
:href="rawPath" :href="rawPath"
class="js-raw-link raw-link" class="js-raw-link raw-link"
> >
{{ s__("Job|Complete Raw") }} {{ s__("Job|Complete Raw") }}
</a> </gl-link>
</template> </template>
</div> </div>
<!-- eo truncate information --> <!-- eo truncate information -->
<div class="controllers float-right"> <div class="controllers float-right">
<!-- links --> <!-- links -->
<a <gl-link
v-if="rawPath" v-if="rawPath"
v-tooltip v-gl-tooltip.body
:title="s__('Job|Show complete raw')" :title="s__('Job|Show complete raw')"
:href="rawPath" :href="rawPath"
class="js-raw-link-controller controllers-buttons" class="js-raw-link-controller controllers-buttons"
data-container="body"
> >
<icon name="doc-text" /> <icon name="doc-text" />
</a> </gl-link>
<a <gl-link
v-if="erasePath" v-if="erasePath"
v-tooltip v-gl-tooltip.body
:title="s__('Job|Erase job log')" :title="s__('Job|Erase job log')"
:href="erasePath" :href="erasePath"
:data-confirm="__('Are you sure you want to erase this build?')" :data-confirm="__('Are you sure you want to erase this build?')"
class="js-erase-link controllers-buttons" class="js-erase-link controllers-buttons"
data-container="body"
data-method="post" data-method="post"
> >
<icon name="remove" /> <icon name="remove" />
</a> </gl-link>
<!-- eo links --> <!-- eo links -->
<!-- scroll buttons --> <!-- scroll buttons -->
<div <div
v-tooltip v-gl-tooltip
:title="s__('Job|Scroll to top')" :title="s__('Job|Scroll to top')"
class="controllers-buttons" class="controllers-buttons"
data-container="body"
> >
<button <gl-button
:disabled="isScrollTopDisabled" :disabled="isScrollTopDisabled"
type="button" type="button"
class="js-scroll-top btn-scroll btn-transparent btn-blank" class="js-scroll-top btn-scroll btn-transparent btn-blank"
@click="handleScrollToTop" @click="handleScrollToTop"
> >
<icon name="scroll_up"/> <icon name="scroll_up" />
</button> </gl-button>
</div> </div>
<div <div
v-tooltip v-gl-tooltip
:title="s__('Job|Scroll to bottom')" :title="s__('Job|Scroll to bottom')"
class="controllers-buttons" class="controllers-buttons"
data-container="body"
> >
<button <gl-button
:disabled="isScrollBottomDisabled" :disabled="isScrollBottomDisabled"
type="button"
class="js-scroll-bottom btn-scroll btn-transparent btn-blank" class="js-scroll-bottom btn-scroll btn-transparent btn-blank"
:class="{ animate: isScrollingDown }" :class="{ animate: isScrollingDown }"
@click="handleScrollToBottom" @click="handleScrollToBottom"
v-html="$options.scrollDown" v-html="$options.scrollDown"
> />
</button>
</div> </div>
<!-- eo scroll buttons --> <!-- eo scroll buttons -->
</div> </div>
......
<script> <script>
import { GlLink } from '@gitlab-org/gitlab-ui';
export default { export default {
name: 'SidebarDetailRow', name: 'SidebarDetailRow',
components: {
GlLink,
},
props: { props: {
title: { title: {
type: String, type: String,
...@@ -41,7 +46,7 @@ export default { ...@@ -41,7 +46,7 @@ export default {
v-if="hasHelpURL" v-if="hasHelpURL"
class="help-button float-right" class="help-button float-right"
> >
<a <gl-link
:href="helpUrl" :href="helpUrl"
target="_blank" target="_blank"
rel="noopener noreferrer nofollow" rel="noopener noreferrer nofollow"
...@@ -50,7 +55,7 @@ export default { ...@@ -50,7 +55,7 @@ export default {
class="fa fa-question-circle" class="fa fa-question-circle"
aria-hidden="true" aria-hidden="true"
></i> ></i>
</a> </gl-link>
</span> </span>
</p> </p>
</template> </template>
<script> <script>
import { GlLink } from '@gitlab-org/gitlab-ui';
/** /**
* Renders Stuck Runners block for job's view. * Renders Stuck Runners block for job's view.
*/ */
export default { export default {
components: {
GlLink,
},
props: { props: {
hasNoRunnersForProject: { hasNoRunnersForProject: {
type: Boolean, type: Boolean,
...@@ -52,12 +56,12 @@ export default { ...@@ -52,12 +56,12 @@ export default {
</p> </p>
{{ __("Go to") }} {{ __("Go to") }}
<a <gl-link
v-if="runnersPath" v-if="runnersPath"
:href="runnersPath" :href="runnersPath"
class="js-runners-path" class="js-runners-path"
> >
{{ __("Runners page") }} {{ __("Runners page") }}
</a> </gl-link>
</div> </div>
</template> </template>
...@@ -390,7 +390,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" ...@@ -390,7 +390,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
:disabled="isSubmitButtonDisabled" :disabled="isSubmitButtonDisabled"
name="button" name="button"
type="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-display="static"
data-toggle="dropdown" data-toggle="dropdown"
aria-label="Open comment type dropdown"> aria-label="Open comment type dropdown">
...@@ -422,7 +422,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" ...@@ -422,7 +422,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
<li :class="{ 'droplab-item-selected': noteType === 'discussion' }"> <li :class="{ 'droplab-item-selected': noteType === 'discussion' }">
<button <button
type="button" type="button"
class="btn btn-transparent" class="btn btn-transparent qa-discussion-option"
@click.prevent="setNoteType('discussion')"> @click.prevent="setNoteType('discussion')">
<i <i
aria-hidden="true" aria-hidden="true"
......
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import Icon from '~/vue_shared/components/icon.vue';
import { mapGetters, mapActions } from 'vuex'; 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 { export default {
components: { components: {
...@@ -12,14 +13,17 @@ export default { ...@@ -12,14 +13,17 @@ export default {
type: Array, type: Array,
required: true, required: true,
}, },
defaultValue: { selectedValue: {
type: Number, type: Number,
default: null, default: null,
required: false, required: false,
}, },
}, },
data() { data() {
return { currentValue: this.defaultValue }; return {
currentValue: this.selectedValue,
defaultValue: DISCUSSION_FILTERS_DEFAULT_VALUE,
};
}, },
computed: { computed: {
...mapGetters(['getNotesDataByProp']), ...mapGetters(['getNotesDataByProp']),
...@@ -28,8 +32,11 @@ export default { ...@@ -28,8 +32,11 @@ export default {
return this.filters.find(filter => filter.value === this.currentValue); return this.filters.find(filter => filter.value === this.currentValue);
}, },
}, },
mounted() {
this.toggleCommentsForm();
},
methods: { methods: {
...mapActions(['filterDiscussion']), ...mapActions(['filterDiscussion', 'setCommentsDisabled']),
selectFilter(value) { selectFilter(value) {
const filter = parseInt(value, 10); const filter = parseInt(value, 10);
...@@ -39,6 +46,10 @@ export default { ...@@ -39,6 +46,10 @@ export default {
if (filter === this.currentValue) return; if (filter === this.currentValue) return;
this.currentValue = filter; this.currentValue = filter;
this.filterDiscussion({ path: this.getNotesDataByProp('discussionsPath'), 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 { ...@@ -73,6 +84,10 @@ export default {
> >
{{ filter.title }} {{ filter.title }}
</button> </button>
<div
v-if="filter.value === defaultValue"
class="dropdown-divider"
></div>
</li> </li>
</ul> </ul>
</div> </div>
......
...@@ -191,7 +191,7 @@ export default { ...@@ -191,7 +191,7 @@ export default {
:data-supports-quick-actions="!isEditing" :data-supports-quick-actions="!isEditing"
name="note[note]" name="note[note]"
class="note-textarea js-gfm-input js-note-text 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" aria-label="Description"
placeholder="Write a comment or drag your files here…" placeholder="Write a comment or drag your files here…"
@keydown.meta.enter="handleUpdate()" @keydown.meta.enter="handleUpdate()"
...@@ -216,6 +216,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea" ...@@ -216,6 +216,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
<input <input
v-model="isUnresolving" v-model="isUnresolving"
type="checkbox" type="checkbox"
class="qa-unresolve-review-discussion"
/> />
{{ __('Unresolve discussion') }} {{ __('Unresolve discussion') }}
</template> </template>
...@@ -225,6 +226,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea" ...@@ -225,6 +226,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
<input <input
v-model="isResolving" v-model="isResolving"
type="checkbox" type="checkbox"
class="qa-resolve-review-discussion"
/> />
{{ __('Resolve discussion') }} {{ __('Resolve discussion') }}
</template> </template>
...@@ -234,7 +236,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea" ...@@ -234,7 +236,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
<button <button
:disabled="isDisabled" :disabled="isDisabled"
type="button" type="button"
class="btn btn-success" class="btn btn-success qa-start-review"
@click="handleAddToReview()"> @click="handleAddToReview()">
<template v-if="hasDrafts"> <template v-if="hasDrafts">
{{ __('Add to review') }} {{ __('Add to review') }}
...@@ -246,7 +248,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea" ...@@ -246,7 +248,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
<button <button
:disabled="isDisabled" :disabled="isDisabled"
type="button" type="button"
class="btn" class="btn qa-comment-now"
@click="handleUpdate()"> @click="handleUpdate()">
{{ __('Add comment now') }} {{ __('Add comment now') }}
</button> </button>
......
...@@ -385,7 +385,7 @@ Please check your network connection and try again.`; ...@@ -385,7 +385,7 @@ Please check your network connection and try again.`;
role="group"> role="group">
<button <button
type="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" title="Add a reply"
@click="showReplyForm">Reply...</button> @click="showReplyForm">Reply...</button>
</div> </div>
......
...@@ -60,6 +60,7 @@ export default { ...@@ -60,6 +60,7 @@ export default {
'getNotesDataByProp', 'getNotesDataByProp',
'discussionCount', 'discussionCount',
'isLoading', 'isLoading',
'commentsDisabled',
]), ]),
noteableType() { noteableType() {
return this.noteableData.noteableType; return this.noteableData.noteableType;
...@@ -206,6 +207,7 @@ export default { ...@@ -206,6 +207,7 @@ export default {
</ul> </ul>
<comment-form <comment-form
v-if="!commentsDisabled"
:noteable-type="noteableType" :noteable-type="noteableType"
:markdown-version="markdownVersion" :markdown-version="markdownVersion"
/> />
......
...@@ -15,6 +15,8 @@ export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest'; ...@@ -15,6 +15,8 @@ export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest';
export const UNRESOLVE_NOTE_METHOD_NAME = 'delete'; export const UNRESOLVE_NOTE_METHOD_NAME = 'delete';
export const RESOLVE_NOTE_METHOD_NAME = 'post'; export const RESOLVE_NOTE_METHOD_NAME = 'post';
export const DESCRIPTION_TYPE = 'changed the description'; 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 = { export const NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE, Issue: ISSUE_NOTEABLE_TYPE,
......
...@@ -6,7 +6,7 @@ export default store => { ...@@ -6,7 +6,7 @@ export default store => {
if (discussionFilterEl) { if (discussionFilterEl) {
const { defaultFilter, notesFilters } = discussionFilterEl.dataset; 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 filterValues = notesFilters ? JSON.parse(notesFilters) : {};
const filters = Object.keys(filterValues).map(entry => ({ const filters = Object.keys(filterValues).map(entry => ({
title: entry, title: entry,
...@@ -24,7 +24,7 @@ export default store => { ...@@ -24,7 +24,7 @@ export default store => {
return createElement('discussion-filter', { return createElement('discussion-filter', {
props: { props: {
filters, filters,
defaultValue, selectedValue,
}, },
}); });
}, },
......
...@@ -364,5 +364,9 @@ export const filterDiscussion = ({ dispatch }, { path, filter }) => { ...@@ -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 // prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {}; export default () => {};
...@@ -195,5 +195,7 @@ export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => { ...@@ -195,5 +195,7 @@ export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => {
export const getDiscussion = state => discussionId => export const getDiscussion = state => discussionId =>
state.discussions.find(discussion => discussion.id === 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 // prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {}; export default () => {};
...@@ -21,6 +21,7 @@ export default () => ({ ...@@ -21,6 +21,7 @@ export default () => ({
noteableData: { noteableData: {
current_user: {}, current_user: {},
}, },
commentsDisabled: false,
}, },
actions, actions,
getters, getters,
......
...@@ -15,6 +15,7 @@ export const UPDATE_DISCUSSION = 'UPDATE_DISCUSSION'; ...@@ -15,6 +15,7 @@ export const UPDATE_DISCUSSION = 'UPDATE_DISCUSSION';
export const SET_DISCUSSION_DIFF_LINES = 'SET_DISCUSSION_DIFF_LINES'; export const SET_DISCUSSION_DIFF_LINES = 'SET_DISCUSSION_DIFF_LINES';
export const SET_NOTES_FETCHED_STATE = 'SET_NOTES_FETCHED_STATE'; export const SET_NOTES_FETCHED_STATE = 'SET_NOTES_FETCHED_STATE';
export const SET_NOTES_LOADING_STATE = 'SET_NOTES_LOADING_STATE'; export const SET_NOTES_LOADING_STATE = 'SET_NOTES_LOADING_STATE';
export const DISABLE_COMMENTS = 'DISABLE_COMMENTS';
// DISCUSSION // DISCUSSION
export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION'; export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION';
......
...@@ -225,4 +225,8 @@ export default { ...@@ -225,4 +225,8 @@ export default {
discussion.truncated_diff_lines = diffLines; discussion.truncated_diff_lines = diffLines;
}, },
[types.DISABLE_COMMENTS](state, value) {
state.commentsDisabled = value;
},
}; };
...@@ -29,7 +29,7 @@ export default { ...@@ -29,7 +29,7 @@ export default {
if (action.scheduled_at) { if (action.scheduled_at) {
const confirmationMessage = sprintf( const confirmationMessage = sprintf(
s__( 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 }, { jobName: action.name },
); );
......
...@@ -5,7 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue'; ...@@ -5,7 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import GfmAutoComplete from '~/gfm_auto_complete'; import GfmAutoComplete from '~/gfm_auto_complete';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import Api from '~/api'; 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 eventHub from './event_hub';
import EmojiMenuInModal from './emoji_menu_in_modal'; import EmojiMenuInModal from './emoji_menu_in_modal';
...@@ -16,6 +16,9 @@ export default { ...@@ -16,6 +16,9 @@ export default {
Icon, Icon,
GlModal, GlModal,
}, },
directives: {
GlTooltip: GlTooltipDirective,
},
props: { props: {
currentEmoji: { currentEmoji: {
type: String, type: String,
......
<script> <script>
import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility'; import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility';
import { GlTooltipDirective } from '@gitlab-org/gitlab-ui';
/** /**
* Counts down to a given end date. * Counts down to a given end date.
*/ */
export default { export default {
directives: {
GlTooltip: GlTooltipDirective,
},
props: { props: {
endDateString: { endDateString: {
type: String, type: String,
......
...@@ -348,6 +348,7 @@ ...@@ -348,6 +348,7 @@
@include media-breakpoint-down(xs) { @include media-breakpoint-down(xs) {
width: 100%; width: 100%;
margin: $btn-side-margin 0;
} }
} }
} }
......
...@@ -322,15 +322,15 @@ ...@@ -322,15 +322,15 @@
width: $contextual-sidebar-width - 1px; width: $contextual-sidebar-width - 1px;
transition: width $sidebar-transition-duration; transition: width $sidebar-transition-duration;
position: fixed; position: fixed;
height: $toggle-sidebar-height;
bottom: 0; bottom: 0;
padding: $gl-padding; padding: 0 $gl-padding;
background-color: $gray-light; background-color: $gray-light;
border: 0; border: 0;
border-top: 1px solid $border-color; border-top: 1px solid $border-color;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
display: flex; display: flex;
align-items: center; align-items: center;
line-height: 1;
svg { svg {
margin-right: 8px; margin-right: 8px;
......
...@@ -291,7 +291,7 @@ ...@@ -291,7 +291,7 @@
/* /*
* Mixin that handles the position of the controls placed on the top bar * 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; display: flex;
font-size: $control-font-size; font-size: $control-font-size;
justify-content: $flex-direction; justify-content: $flex-direction;
...@@ -304,8 +304,9 @@ ...@@ -304,8 +304,9 @@
svg { svg {
width: 15px; width: 15px;
height: 15px; height: 15px;
display: block; display: $svg-display;
fill: $gl-text-color; fill: $gl-text-color;
top: $svg-top;
} }
.controllers-buttons { .controllers-buttons {
......
...@@ -147,3 +147,9 @@ table { ...@@ -147,3 +147,9 @@ table {
} }
} }
} }
.top-area + .content-list {
th {
border-top: 0;
}
}
...@@ -10,6 +10,7 @@ $sidebar-breakpoint: 1024px; ...@@ -10,6 +10,7 @@ $sidebar-breakpoint: 1024px;
$default-transition-duration: 0.15s; $default-transition-duration: 0.15s;
$contextual-sidebar-width: 220px; $contextual-sidebar-width: 220px;
$contextual-sidebar-collapsed-width: 50px; $contextual-sidebar-collapsed-width: 50px;
$toggle-sidebar-height: 48px;
/* /*
* Color schema * Color schema
......
...@@ -57,10 +57,6 @@ ...@@ -57,10 +57,6 @@
.top-bar { .top-bar {
@include build-trace-top-bar(35px); @include build-trace-top-bar(35px);
&.sidebar-expanded {
margin-right: calc(#{$gutter-width} - 16px);
}
.truncated-info { .truncated-info {
.truncated-info-size { .truncated-info-size {
margin: 0 5px; margin: 0 5px;
...@@ -74,7 +70,7 @@ ...@@ -74,7 +70,7 @@
} }
.controllers { .controllers {
@include build-controllers(15px, center, false, 0); @include build-controllers(15px, center, false, 0, inline, 0);
} }
} }
......
...@@ -44,11 +44,6 @@ ...@@ -44,11 +44,6 @@
margin: 0; margin: 0;
} }
.icon-play {
height: 13px;
width: 12px;
}
.external-url, .external-url,
.dropdown-new { .dropdown-new {
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
...@@ -526,7 +521,7 @@ ...@@ -526,7 +521,7 @@
} }
.arrow-shadow { .arrow-shadow {
content: ""; content: '';
position: absolute; position: absolute;
width: 7px; width: 7px;
height: 7px; height: 7px;
......
...@@ -39,10 +39,6 @@ ...@@ -39,10 +39,6 @@
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
svg {
vertical-align: middle;
}
} }
.next-run-cell { .next-run-cell {
...@@ -52,6 +48,10 @@ ...@@ -52,6 +48,10 @@
a { a {
color: $text-color; color: $text-color;
} }
svg {
vertical-align: middle;
}
} }
.pipeline-schedules-user-callout { .pipeline-schedules-user-callout {
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class PersonalAccessTokensFinder class PersonalAccessTokensFinder
attr_accessor :params 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 = {}) def initialize(params = {})
@params = params @params = params
......
...@@ -116,6 +116,7 @@ module ApplicationSettingsHelper ...@@ -116,6 +116,7 @@ module ApplicationSettingsHelper
:akismet_api_key, :akismet_api_key,
:akismet_enabled, :akismet_enabled,
:allow_local_requests_from_hooks_and_services, :allow_local_requests_from_hooks_and_services,
:archive_builds_in_human_readable,
:authorized_keys_enabled, :authorized_keys_enabled,
:auto_devops_enabled, :auto_devops_enabled,
:auto_devops_domain, :auto_devops_domain,
......
...@@ -109,6 +109,8 @@ module IconsHelper ...@@ -109,6 +109,8 @@ module IconsHelper
def file_type_icon_class(type, mode, name) def file_type_icon_class(type, mode, name)
if type == 'folder' if type == 'folder'
icon_class = 'folder' icon_class = 'folder'
elsif type == 'archive'
icon_class = 'archive'
elsif mode == '120000' elsif mode == '120000'
icon_class = 'share' icon_class = 'share'
else else
......
...@@ -31,11 +31,21 @@ module TreeHelper ...@@ -31,11 +31,21 @@ module TreeHelper
# mode - File unix mode # mode - File unix mode
# name - File name # name - File name
def tree_icon(type, mode, 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 end
def tree_hex_class(content) # Using Rails `*_path` methods can be slow, especially when generating
"file_#{hexdigest(content.name)}" # 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 end
# Simple shortcut to File.join # Simple shortcut to File.join
...@@ -142,4 +152,8 @@ module TreeHelper ...@@ -142,4 +152,8 @@ module TreeHelper
def selected_branch def selected_branch
@branch_name || tree_edit_branch @branch_name || tree_edit_branch
end end
def relative_url_root
Gitlab.config.gitlab.relative_url_root.presence || '/'
end
end end
...@@ -5,6 +5,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -5,6 +5,8 @@ class ApplicationSetting < ActiveRecord::Base
include CacheMarkdownField include CacheMarkdownField
include TokenAuthenticatable include TokenAuthenticatable
include IgnorableColumn include IgnorableColumn
include ChronicDurationAttribute
prepend EE::ApplicationSetting prepend EE::ApplicationSetting
add_authentication_token_field :runners_registration_token add_authentication_token_field :runners_registration_token
...@@ -46,6 +48,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -46,6 +48,8 @@ class ApplicationSetting < ActiveRecord::Base
default_value_for :id, 1 default_value_for :id, 1
chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds
validates :uuid, presence: true validates :uuid, presence: true
validates :session_expire_delay, validates :session_expire_delay,
...@@ -185,6 +189,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -185,6 +189,10 @@ class ApplicationSetting < ActiveRecord::Base
validates :user_default_internal_regex, js_regex: true, allow_nil: true 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| SUPPORTED_KEY_TYPES.each do |type|
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type } validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
end end
...@@ -442,6 +450,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -442,6 +450,10 @@ class ApplicationSetting < ActiveRecord::Base
latest_terms latest_terms
end end
def archive_builds_older_than
archive_builds_in_seconds.seconds.ago if archive_builds_in_seconds
end
private private
def ensure_uuid! def ensure_uuid!
......
...@@ -249,17 +249,37 @@ module Ci ...@@ -249,17 +249,37 @@ module Ci
.fabricate! .fabricate!
end end
def other_actions def other_manual_actions
pipeline.manual_actions.where.not(name: name) pipeline.manual_actions.where.not(name: name)
end end
def other_scheduled_actions
pipeline.scheduled_actions.where.not(name: name)
end
def pages_generator? def pages_generator?
Gitlab.config.pages.enabled && Gitlab.config.pages.enabled &&
self.name == 'pages' self.name == 'pages'
end 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? def playable?
action? && (manual? || scheduled? || retryable?) action? && !archived? && (manual? || scheduled? || retryable?)
end end
def schedulable? def schedulable?
...@@ -287,7 +307,7 @@ module Ci ...@@ -287,7 +307,7 @@ module Ci
end end
def retryable? def retryable?
success? || failed? || canceled? !archived? && (success? || failed? || canceled?)
end end
def retries_count def retries_count
...@@ -295,7 +315,7 @@ module Ci ...@@ -295,7 +315,7 @@ module Ci
end end
def retries_max def retries_max
self.options.fetch(:retry, 0).to_i self.options.to_h.fetch(:retry, 0).to_i
end end
def latest? def latest?
......
...@@ -17,7 +17,7 @@ module Ci ...@@ -17,7 +17,7 @@ module Ci
metadata: nil, metadata: nil,
trace: nil, trace: nil,
junit: 'junit.xml', junit: 'junit.xml',
codequality: 'codequality.json', codequality: 'gl-code-quality-report.json',
sast: 'gl-sast-report.json', sast: 'gl-sast-report.json',
dependency_scanning: 'gl-dependency-scanning-report.json', dependency_scanning: 'gl-dependency-scanning-report.json',
container_scanning: 'gl-container-scanning-report.json', container_scanning: 'gl-container-scanning-report.json',
......
...@@ -53,7 +53,8 @@ class CommitStatus < ActiveRecord::Base ...@@ -53,7 +53,8 @@ class CommitStatus < ActiveRecord::Base
missing_dependency_failure: 5, missing_dependency_failure: 5,
runner_unsupported: 6, runner_unsupported: 6,
stale_schedule: 7, stale_schedule: 7,
job_execution_timeout: 8 job_execution_timeout: 8,
archived_failure: 9
}.merge(EE_FAILURE_REASONS) }.merge(EE_FAILURE_REASONS)
## ##
...@@ -169,16 +170,18 @@ class CommitStatus < ActiveRecord::Base ...@@ -169,16 +170,18 @@ class CommitStatus < ActiveRecord::Base
false false
end end
# To be overridden when inherrited from
def retryable? def retryable?
false false
end end
# To be overridden when inherrited from
def cancelable? def cancelable?
false false
end end
def archived?
false
end
def stuck? def stuck?
false false
end end
......
...@@ -4,6 +4,7 @@ class DeployToken < ActiveRecord::Base ...@@ -4,6 +4,7 @@ class DeployToken < ActiveRecord::Base
include Expirable include Expirable
include TokenAuthenticatable include TokenAuthenticatable
include PolicyActor include PolicyActor
include Gitlab::Utils::StrongMemoize
add_authentication_token_field :token add_authentication_token_field :token
AVAILABLE_SCOPES = %i(read_repository read_registry).freeze AVAILABLE_SCOPES = %i(read_repository read_registry).freeze
...@@ -49,7 +50,9 @@ class DeployToken < ActiveRecord::Base ...@@ -49,7 +50,9 @@ class DeployToken < ActiveRecord::Base
# to a single project, later we're going to extend # to a single project, later we're going to extend
# that to be for multiple projects and namespaces. # that to be for multiple projects and namespaces.
def project def project
projects.first strong_memoize(:project) do
projects.first
end
end end
def expires_at def expires_at
......
...@@ -55,7 +55,11 @@ class Deployment < ActiveRecord::Base ...@@ -55,7 +55,11 @@ class Deployment < ActiveRecord::Base
end end
def manual_actions 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 end
def includes_commit?(commit) def includes_commit?(commit)
......
...@@ -119,6 +119,8 @@ class Note < ActiveRecord::Base ...@@ -119,6 +119,8 @@ class Note < ActiveRecord::Base
case notes_filter case notes_filter
when UserPreference::NOTES_FILTERS[:only_comments] when UserPreference::NOTES_FILTERS[:only_comments]
user user
when UserPreference::NOTES_FILTERS[:only_activity]
system
else else
all all
end 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 ...@@ -4,6 +4,15 @@ module Postgresql
class ReplicationSlot < ActiveRecord::Base class ReplicationSlot < ActiveRecord::Base
self.table_name = 'pg_replication_slots' 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 # Returns true if the lag observed across all replication slots exceeds a
# given threshold. # given threshold.
# #
...@@ -11,6 +20,8 @@ module Postgresql ...@@ -11,6 +20,8 @@ module Postgresql
# statistics it takes between 1 and 5 seconds to replicate around # statistics it takes between 1 and 5 seconds to replicate around
# 100 MB of data. # 100 MB of data.
def self.lag_too_great?(max = 100.megabytes) def self.lag_too_great?(max = 100.megabytes)
return false unless in_use?
lag_function = "#{Gitlab::Database.pg_wal_lsn_diff}" \ lag_function = "#{Gitlab::Database.pg_wal_lsn_diff}" \
"(#{Gitlab::Database.pg_current_wal_insert_lsn}(), restart_lsn)::bigint" "(#{Gitlab::Database.pg_current_wal_insert_lsn}(), restart_lsn)::bigint"
......
...@@ -98,8 +98,7 @@ class Project < ActiveRecord::Base ...@@ -98,8 +98,7 @@ class Project < ActiveRecord::Base
unless: :ci_cd_settings, unless: :ci_cd_settings,
if: proc { ProjectCiCdSetting.available? } if: proc { ProjectCiCdSetting.available? }
after_create :set_last_activity_at after_create :set_timestamps_for_create
after_create :set_last_repository_updated_at
after_update :update_forks_visibility_level after_update :update_forks_visibility_level
before_destroy :remove_private_deploy_keys before_destroy :remove_private_deploy_keys
...@@ -127,6 +126,7 @@ class Project < ActiveRecord::Base ...@@ -127,6 +126,7 @@ class Project < ActiveRecord::Base
alias_attribute :title, :name alias_attribute :title, :name
# Relations # Relations
belongs_to :pool_repository
belongs_to :creator, class_name: 'User' belongs_to :creator, class_name: 'User'
belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id' belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id'
belongs_to :namespace belongs_to :namespace
...@@ -2101,13 +2101,8 @@ class Project < ActiveRecord::Base ...@@ -2101,13 +2101,8 @@ class Project < ActiveRecord::Base
gitlab_shell.exists?(repository_storage, "#{disk_path}.git") gitlab_shell.exists?(repository_storage, "#{disk_path}.git")
end end
# set last_activity_at to the same as created_at def set_timestamps_for_create
def set_last_activity_at update_columns(last_activity_at: self.created_at, last_repository_updated_at: self.created_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)
end end
def cross_namespace_reference?(from) 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 ...@@ -460,12 +460,6 @@ class User < ActiveRecord::Base
by_username(username).take! by_username(username).take!
end 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. # Returns a user for the given SSH key.
def find_by_ssh_key_id(key_id) def find_by_ssh_key_id(key_id)
Key.find_by(id: key_id)&.user Key.find_by(id: key_id)&.user
......
...@@ -4,7 +4,7 @@ class UserPreference < ActiveRecord::Base ...@@ -4,7 +4,7 @@ class UserPreference < ActiveRecord::Base
# We could use enums, but Rails 4 doesn't support multiple # We could use enums, but Rails 4 doesn't support multiple
# enum options with same name for multiple fields, also it creates # enum options with same name for multiple fields, also it creates
# extra methods that aren't really needed here. # 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 belongs_to :user
...@@ -14,7 +14,8 @@ class UserPreference < ActiveRecord::Base ...@@ -14,7 +14,8 @@ class UserPreference < ActiveRecord::Base
def notes_filters def notes_filters
{ {
s_('Notes|Show all activity') => NOTES_FILTERS[:all_notes], 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
end end
......
...@@ -22,12 +22,17 @@ module Ci ...@@ -22,12 +22,17 @@ module Ci
@subject.project.branch_allows_collaboration?(@user, @subject.ref) @subject.project.branch_allows_collaboration?(@user, @subject.ref)
end end
condition(:archived, scope: :subject) do
@subject.archived?
end
condition(:terminal, scope: :subject) do condition(:terminal, scope: :subject) do
@subject.has_terminal? @subject.has_terminal?
end end
rule { protected_ref }.policy do rule { protected_ref | archived }.policy do
prevent :update_build prevent :update_build
prevent :update_commit_status
prevent :erase_build prevent :erase_build
end end
......
...@@ -2,4 +2,13 @@ ...@@ -2,4 +2,13 @@
class DeploymentPolicy < BasePolicy class DeploymentPolicy < BasePolicy
delegate { @subject.project } 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 end
...@@ -9,7 +9,8 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated ...@@ -9,7 +9,8 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
missing_dependency_failure: 'There has been a missing dependency failure', missing_dependency_failure: 'There has been a missing dependency failure',
runner_unsupported: 'Your runner is outdated, please upgrade your runner', runner_unsupported: 'Your runner is outdated, please upgrade your runner',
stale_schedule: 'Delayed job could not be executed by some reason, please try again', 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 }.freeze
private_constant :CALLOUT_FAILURE_MESSAGES private_constant :CALLOUT_FAILURE_MESSAGES
...@@ -31,6 +32,6 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated ...@@ -31,6 +32,6 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
end end
def unrecoverable? def unrecoverable?
script_failure? || missing_dependency_failure? script_failure? || missing_dependency_failure? || archived_failure?
end end
end end
...@@ -25,4 +25,5 @@ class DeploymentEntity < Grape::Entity ...@@ -25,4 +25,5 @@ class DeploymentEntity < Grape::Entity
expose :commit, using: CommitEntity expose :commit, using: CommitEntity
expose :deployable, using: JobEntity expose :deployable, using: JobEntity
expose :manual_actions, using: JobEntity expose :manual_actions, using: JobEntity
expose :scheduled_actions, using: JobEntity
end end
...@@ -7,6 +7,7 @@ class JobEntity < Grape::Entity ...@@ -7,6 +7,7 @@ class JobEntity < Grape::Entity
expose :name expose :name
expose :started?, as: :started expose :started?, as: :started
expose :archived?, as: :archived
expose :build_path do |build| expose :build_path do |build|
build_path(build) build_path(build)
......
...@@ -7,4 +7,8 @@ class UserPreferenceEntity < Grape::Entity ...@@ -7,4 +7,8 @@ class UserPreferenceEntity < Grape::Entity
expose :notes_filters do |user_preference| expose :notes_filters do |user_preference|
UserPreference.notes_filters UserPreference.notes_filters
end end
expose :default_notes_filter do |user_preference|
UserPreference::NOTES_FILTERS[:all_notes]
end
end end
...@@ -84,6 +84,11 @@ module Ci ...@@ -84,6 +84,11 @@ module Ci
return false return false
end end
if build.archived?
build.drop!(:archived_failure)
return false
end
build.run! build.run!
true true
end end
......
...@@ -44,5 +44,13 @@ ...@@ -44,5 +44,13 @@
The default unit is in seconds, but you can define an alternative. For example: The default unit is in seconds, but you can define an alternative. For example:
<code>4 mins 2 sec</code>, <code>2h42min</code>. <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') = 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" = f.submit 'Save changes', class: "btn btn-success"
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- subscribed = params[:subscribed] - subscribed = params[:subscribed]
- labels_or_filters = @labels.exists? || search.present? || subscribed.present? - labels_or_filters = @labels.exists? || search.present? || subscribed.present?
- if can_admin_label - if @labels.present? && can_admin_label
- content_for(:header_content) do - content_for(:header_content) do
.nav-controls .nav-controls
= link_to _('New label'), new_group_label_path(@group), class: "btn btn-success" = 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') - 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 = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build has-tooltip', title: tooltip do
- if deployment.last? - if deployment.last?
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- subscribed = params[:subscribed] - subscribed = params[:subscribed]
- labels_or_filters = @labels.exists? || @prioritized_labels.exists? || search.present? || subscribed.present? - 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 - content_for(:header_content) do
.nav-controls .nav-controls
= link_to _('New label'), new_project_label_path(@project), class: "btn btn-success qa-label-create-new" = link_to _('New label'), new_project_label_path(@project), class: "btn btn-success qa-label-create-new"
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
.fade-left= icon('angle-left') .fade-left= icon('angle-left')
.fade-right= icon('angle-right') .fade-right= icon('angle-right')
%ul.merge-request-tabs.nav-tabs.nav.nav-links.scrolling-tabs %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 = tab_link_for @merge_request, :show, force_link: @commit.present? do
Discussion Discussion
%span.badge.badge-pill= @merge_request.related_notes.user.count %span.badge.badge-pill= @merge_request.related_notes.user.count
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
= tab_link_for @merge_request, :pipelines do = tab_link_for @merge_request, :pipelines do
Pipelines Pipelines
%span.badge.badge-pill.js-pipelines-mr-count= @pipelines.size %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 = tab_link_for @merge_request, :diffs do
Changes Changes
%span.badge.badge-pill= @merge_request.diff_size %span.badge.badge-pill= @merge_request.diff_size
......
...@@ -61,12 +61,14 @@ ...@@ -61,12 +61,14 @@
%td.responsive-table-cell.build-failure{ data: { column: _("Failure")} } %td.responsive-table-cell.build-failure{ data: { column: _("Failure")} }
= build.present.callout_failure_message = build.present.callout_failure_message
%td.responsive-table-cell.build-actions %td.responsive-table-cell.build-actions
= link_to retry_project_job_path(build.project, build, return_to: request.original_url), method: :post, title: _('Retry'), class: 'btn btn-build' do - if can?(current_user, :update_build, job)
= icon('repeat') = link_to retry_project_job_path(build.project, build, return_to: request.original_url), method: :post, title: _('Retry'), class: 'btn btn-build' do
%tr.build-trace-row.responsive-table-border-end = icon('repeat')
%td - if can?(current_user, :read_build, job)
%td.responsive-table-cell.build-trace-container{ colspan: 4 } %tr.build-trace-row.responsive-table-border-end
%pre.build-trace.build-trace-rounded %td
%code.bash.js-build-output %td.responsive-table-cell.build-trace-container{ colspan: 4 }
= build_summary(build) %pre.build-trace.build-trace-rounded
%code.bash.js-build-output
= build_summary(build)
= render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project = render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project
- 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 - tree_row_name = tree_row.name
= render partial: 'projects/tree/tree_item', object: tree_row, as: 'tree_item', locals: { type: 'folder' } - tree_row_type = tree_row.type
- elsif tree_row.type == :blob
= render partial: 'projects/tree/blob_item', object: tree_row, as: 'blob_item', locals: { type: 'file' } %tr{ class: "tree-item file_#{hexdigest(tree_row_name)}" }
- elsif tree_row.type == :commit %td.tree-item-file-name
= render partial: 'projects/tree/submodule_item', object: tree_row, as: 'submodule_item' - 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 @@ ...@@ -6,6 +6,9 @@
.text-content .text-content
%h4= _("Labels can be applied to issues and merge requests to categorize them.") %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.") %p= _("You can also star a label to make it a priority label.")
- if can?(current_user, :admin_label, @project) .text-center
= link_to _('New label'), new_project_label_path(@project), class: 'btn btn-success', title: _('New label'), id: 'new_label_link' - if can?(current_user, :admin_label, @project)
= 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' = 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: merge_request:
author: author:
type: fixed 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 title: Remove gitlab-ui's tooltip from global
merge_request: 7990 merge_request:
author: author:
type: performance 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 ...@@ -45,4 +45,6 @@ Rails.application.configure do
# Do not log asset requests # Do not log asset requests
config.assets.quiet = true config.assets.quiet = true
config.allow_concurrency = defined?(::Puma)
end end
...@@ -83,5 +83,5 @@ Rails.application.configure do ...@@ -83,5 +83,5 @@ Rails.application.configure do
config.eager_load = true config.eager_load = true
config.allow_concurrency = false config.allow_concurrency = defined?(::Puma)
end 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