Commit 8445acfc authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-to-ee' into 'master'

CE Upstream - Monday

Closes #4056 and gitlab-qa#113

See merge request gitlab-org/gitlab-ee!3470
parents fa019f65 42b48e0f
...@@ -500,7 +500,7 @@ migration:path-mysql: ...@@ -500,7 +500,7 @@ migration:path-mysql:
<<: *pull-cache <<: *pull-cache
stage: test stage: test
script: script:
- bundle exec rake db:rollback STEP=120 - bundle exec rake db:rollback STEP=119
- bundle exec rake db:migrate - bundle exec rake db:migrate
db:rollback-pg: db:rollback-pg:
......
...@@ -355,7 +355,7 @@ group :development, :test do ...@@ -355,7 +355,7 @@ group :development, :test do
gem 'benchmark-ips', '~> 2.3.0', require: false gem 'benchmark-ips', '~> 2.3.0', require: false
gem 'license_finder', '~> 2.1.0', require: false gem 'license_finder', '~> 3.1', require: false
gem 'knapsack', '~> 1.11.0' gem 'knapsack', '~> 1.11.0'
gem 'activerecord_sane_schema_dumper', '0.2' gem 'activerecord_sane_schema_dumper', '0.2'
......
...@@ -91,6 +91,7 @@ GEM ...@@ -91,6 +91,7 @@ GEM
bindata (2.4.1) bindata (2.4.1)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
blankslate (2.1.2.4)
bootstrap-sass (3.3.6) bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1) autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4) sass (>= 3.3.4)
...@@ -479,11 +480,13 @@ GEM ...@@ -479,11 +480,13 @@ GEM
actionmailer (>= 3.2) actionmailer (>= 3.2)
letter_opener (~> 1.0) letter_opener (~> 1.0)
railties (>= 3.2) railties (>= 3.2)
license_finder (2.1.0) license_finder (3.1.1)
bundler bundler
httparty httparty
rubyzip rubyzip
thor thor
toml (= 0.1.2)
with_env (> 1.0)
xml-simple xml-simple
licensee (8.7.0) licensee (8.7.0)
rugged (~> 0.24) rugged (~> 0.24)
...@@ -600,6 +603,8 @@ GEM ...@@ -600,6 +603,8 @@ GEM
activerecord (>= 4.0, < 5.2) activerecord (>= 4.0, < 5.2)
parser (2.4.0.0) parser (2.4.0.0)
ast (~> 2.2) ast (~> 2.2)
parslet (1.5.0)
blankslate (~> 2.0)
path_expander (1.0.1) path_expander (1.0.1)
peek (1.0.1) peek (1.0.1)
concurrent-ruby (>= 0.9.0) concurrent-ruby (>= 0.9.0)
...@@ -928,6 +933,8 @@ GEM ...@@ -928,6 +933,8 @@ GEM
tilt (2.0.6) tilt (2.0.6)
timecop (0.8.1) timecop (0.8.1)
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
toml (0.1.2)
parslet (~> 1.5.0)
toml-rb (0.3.15) toml-rb (0.3.15)
citrus (~> 3.0, > 3.0) citrus (~> 3.0, > 3.0)
truncato (0.7.10) truncato (0.7.10)
...@@ -982,6 +989,7 @@ GEM ...@@ -982,6 +989,7 @@ GEM
builder builder
expression_parser expression_parser
rinku rinku
with_env (1.1.0)
xml-simple (1.1.5) xml-simple (1.1.5)
xpath (2.1.0) xpath (2.1.0)
nokogiri (~> 1.3) nokogiri (~> 1.3)
...@@ -1095,7 +1103,7 @@ DEPENDENCIES ...@@ -1095,7 +1103,7 @@ DEPENDENCIES
knapsack (~> 1.11.0) knapsack (~> 1.11.0)
kubeclient (~> 2.2.0) kubeclient (~> 2.2.0)
letter_opener_web (~> 1.3.0) letter_opener_web (~> 1.3.0)
license_finder (~> 2.1.0) license_finder (~> 3.1)
licensee (~> 8.7.0) licensee (~> 8.7.0)
lograge (~> 0.5) lograge (~> 0.5)
loofah (~> 2.0.3) loofah (~> 2.0.3)
......
app/assets/images/emoji.png

1.16 MB | W: | H:

app/assets/images/emoji.png

1.16 MB | W: | H:

app/assets/images/emoji.png
app/assets/images/emoji.png
app/assets/images/emoji.png
app/assets/images/emoji.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/emoji/mrs_claus.png

2.15 KB | W: | H:

app/assets/images/emoji/mrs_claus.png

3.26 KB | W: | H:

app/assets/images/emoji/mrs_claus.png
app/assets/images/emoji/mrs_claus.png
app/assets/images/emoji/mrs_claus.png
app/assets/images/emoji/mrs_claus.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/emoji@2x.png

2.84 MB | W: | H:

app/assets/images/emoji@2x.png

2.84 MB | W: | H:

app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -79,10 +79,6 @@ ...@@ -79,10 +79,6 @@
.catch(() => Flash('Unable to build Slack link.')); .catch(() => Flash('Unable to build Slack link.'));
}, },
}, },
mounted() {
GitlabSlackService.init();
},
}; };
</script> </script>
......
import axios from 'axios'; import axios from '../../lib/utils/axios_utils';
import setAxiosCsrfToken from '../../lib/utils/axios_utils';
export default { export default {
init() {
setAxiosCsrfToken();
},
addToSlack(url, projectId) { addToSlack(url, projectId) {
return axios.get(url, { return axios.get(url, {
params: { params: {
......
import axios from 'axios'; import axios from '../../lib/utils/axios_utils';
import setAxiosCsrfToken from '../../lib/utils/axios_utils';
export default class ClusterService { export default class ClusterService {
constructor(options = {}) { constructor(options = {}) {
setAxiosCsrfToken();
this.options = options; this.options = options;
this.appInstallEndpointMap = { this.appInstallEndpointMap = {
helm: this.options.installHelmEndpoint, helm: this.options.installHelmEndpoint,
...@@ -18,7 +15,6 @@ export default class ClusterService { ...@@ -18,7 +15,6 @@ export default class ClusterService {
} }
installApplication(appId) { installApplication(appId) {
const endpoint = this.appInstallEndpointMap[appId]; return axios.post(this.appInstallEndpointMap[appId]);
return axios.post(endpoint);
} }
} }
...@@ -367,7 +367,8 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -367,7 +367,8 @@ import initGroupAnalytics from './init_group_analytics';
container: '.js-commit-pipeline-graph', container: '.js-commit-pipeline-graph',
}).bindEvents(); }).bindEvents();
initNotes(); initNotes();
initChangesDropdown(); const stickyBarPaddingTop = 16;
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - stickyBarPaddingTop);
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
break; break;
case 'projects:commit:pipelines': case 'projects:commit:pipelines':
......
...@@ -7,6 +7,17 @@ function isFlagEmoji(emojiUnicode) { ...@@ -7,6 +7,17 @@ function isFlagEmoji(emojiUnicode) {
return emojiUnicode.length === 4 && cp >= flagACodePoint && cp <= flagZCodePoint; return emojiUnicode.length === 4 && cp >= flagACodePoint && cp <= flagZCodePoint;
} }
// Tested on mac OS 10.12.6 and Windows 10 FCU, it renders as two separate characters
const baseFlagCodePoint = 127987; // parseInt('1F3F3', 16)
const rainbowCodePoint = 127752; // parseInt('1F308', 16)
function isRainbowFlagEmoji(emojiUnicode) {
const characters = Array.from(emojiUnicode);
// Length 4 because flags are made of 2 characters which are surrogate pairs
return emojiUnicode.length === 4 &&
characters[0].codePointAt(0) === baseFlagCodePoint &&
characters[1].codePointAt(0) === rainbowCodePoint;
}
// Chrome <57 renders keycaps oddly // Chrome <57 renders keycaps oddly
// See https://bugs.chromium.org/p/chromium/issues/detail?id=632294 // See https://bugs.chromium.org/p/chromium/issues/detail?id=632294
// Same issue on Windows also fixed in Chrome 57, http://i.imgur.com/rQF7woO.png // Same issue on Windows also fixed in Chrome 57, http://i.imgur.com/rQF7woO.png
...@@ -57,9 +68,11 @@ function isPersonZwjEmoji(emojiUnicode) { ...@@ -57,9 +68,11 @@ function isPersonZwjEmoji(emojiUnicode) {
// in `isEmojiUnicodeSupported` logic // in `isEmojiUnicodeSupported` logic
function checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) { function checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) {
const isFlagResult = isFlagEmoji(emojiUnicode); const isFlagResult = isFlagEmoji(emojiUnicode);
const isRainbowFlagResult = isRainbowFlagEmoji(emojiUnicode);
return ( return (
(unicodeSupportMap.flag && isFlagResult) || (unicodeSupportMap.flag && isFlagResult) ||
!isFlagResult (unicodeSupportMap.rainbowFlag && isRainbowFlagResult) ||
(!isFlagResult && !isRainbowFlagResult)
); );
} }
...@@ -113,6 +126,7 @@ function isEmojiUnicodeSupported(unicodeSupportMap = {}, emojiUnicode, unicodeVe ...@@ -113,6 +126,7 @@ function isEmojiUnicodeSupported(unicodeSupportMap = {}, emojiUnicode, unicodeVe
export { export {
isEmojiUnicodeSupported as default, isEmojiUnicodeSupported as default,
isFlagEmoji, isFlagEmoji,
isRainbowFlagEmoji,
isKeycapEmoji, isKeycapEmoji,
isSkinToneComboEmoji, isSkinToneComboEmoji,
isHorceRacingSkinToneComboEmoji, isHorceRacingSkinToneComboEmoji,
......
import AccessorUtilities from '../../lib/utils/accessor'; import AccessorUtilities from '../../lib/utils/accessor';
const GL_EMOJI_VERSION = '0.2.0';
const unicodeSupportTestMap = { const unicodeSupportTestMap = {
// man, student (emojione does not have any of these yet), http://emojipedia.org/emoji-zwj-sequences/ // man, student (emojione does not have any of these yet), http://emojipedia.org/emoji-zwj-sequences/
// occupationZwj: '\u{1F468}\u{200D}\u{1F393}', // occupationZwj: '\u{1F468}\u{200D}\u{1F393}',
...@@ -13,6 +15,7 @@ const unicodeSupportTestMap = { ...@@ -13,6 +15,7 @@ const unicodeSupportTestMap = {
horseRacing: '\u{1F3C7}\u{1F3FF}', horseRacing: '\u{1F3C7}\u{1F3FF}',
// US flag, http://emojipedia.org/flags/ // US flag, http://emojipedia.org/flags/
flag: '\u{1F1FA}\u{1F1F8}', flag: '\u{1F1FA}\u{1F1F8}',
rainbowFlag: '\u{1F3F3}\u{1F308}',
// http://emojipedia.org/modifiers/ // http://emojipedia.org/modifiers/
skinToneModifier: [ skinToneModifier: [
// spy_tone5 // spy_tone5
...@@ -141,23 +144,31 @@ function generateUnicodeSupportMap(testMap) { ...@@ -141,23 +144,31 @@ function generateUnicodeSupportMap(testMap) {
} }
export default function getUnicodeSupportMap() { export default function getUnicodeSupportMap() {
let unicodeSupportMap;
let userAgentFromCache;
const isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); const isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
if (isLocalStorageAvailable) userAgentFromCache = window.localStorage.getItem('gl-emoji-user-agent'); let glEmojiVersionFromCache;
let userAgentFromCache;
if (isLocalStorageAvailable) {
glEmojiVersionFromCache = window.localStorage.getItem('gl-emoji-version');
userAgentFromCache = window.localStorage.getItem('gl-emoji-user-agent');
}
let unicodeSupportMap;
try { try {
unicodeSupportMap = JSON.parse(window.localStorage.getItem('gl-emoji-unicode-support-map')); unicodeSupportMap = JSON.parse(window.localStorage.getItem('gl-emoji-unicode-support-map'));
} catch (err) { } catch (err) {
// swallow // swallow
} }
if (!unicodeSupportMap || userAgentFromCache !== navigator.userAgent) { if (
!unicodeSupportMap ||
glEmojiVersionFromCache !== GL_EMOJI_VERSION ||
userAgentFromCache !== navigator.userAgent
) {
unicodeSupportMap = generateUnicodeSupportMap(unicodeSupportTestMap); unicodeSupportMap = generateUnicodeSupportMap(unicodeSupportTestMap);
if (isLocalStorageAvailable) { if (isLocalStorageAvailable) {
window.localStorage.setItem('gl-emoji-version', GL_EMOJI_VERSION);
window.localStorage.setItem('gl-emoji-user-agent', navigator.userAgent); window.localStorage.setItem('gl-emoji-user-agent', navigator.userAgent);
window.localStorage.setItem('gl-emoji-unicode-support-map', JSON.stringify(unicodeSupportMap)); window.localStorage.setItem('gl-emoji-unicode-support-map', JSON.stringify(unicodeSupportMap));
} }
......
...@@ -29,8 +29,8 @@ export default class JobMediator { ...@@ -29,8 +29,8 @@ export default class JobMediator {
this.poll = new Poll({ this.poll = new Poll({
resource: this.service, resource: this.service,
method: 'getJob', method: 'getJob',
successCallback: this.successCallback.bind(this), successCallback: response => this.successCallback(response),
errorCallback: this.errorCallback.bind(this), errorCallback: () => this.errorCallback(),
}); });
if (!Visibility.hidden()) { if (!Visibility.hidden()) {
...@@ -57,7 +57,7 @@ export default class JobMediator { ...@@ -57,7 +57,7 @@ export default class JobMediator {
successCallback(response) { successCallback(response) {
this.state.isLoading = false; this.state.isLoading = false;
return response.json().then(data => this.store.storeJob(data)); return this.store.storeJob(response.data);
} }
errorCallback() { errorCallback() {
......
import Vue from 'vue'; import axios from '../../lib/utils/axios_utils';
import VueResource from 'vue-resource';
Vue.use(VueResource);
export default class JobService { export default class JobService {
constructor(endpoint) { constructor(endpoint) {
this.job = Vue.resource(endpoint); this.job = endpoint;
} }
getJob() { getJob() {
return this.job.get(); return axios.get(this.job);
} }
} }
import axios from 'axios'; import axios from 'axios';
import csrf from './csrf'; import csrf from './csrf';
export default function setAxiosCsrfToken() { axios.defaults.headers.common[csrf.headerKey] = csrf.token;
axios.defaults.headers.common[csrf.headerKey] = csrf.token;
} // Maintain a global counter for active requests
// see: spec/support/wait_for_requests.rb
axios.interceptors.request.use((config) => {
window.activeVueResources = window.activeVueResources || 0;
window.activeVueResources += 1;
return config;
});
// Remove the global counter
axios.interceptors.response.use((config) => {
window.activeVueResources -= 1;
return config;
});
export default axios;
...@@ -3,7 +3,9 @@ import { normalizeHeaders } from './common_utils'; ...@@ -3,7 +3,9 @@ import { normalizeHeaders } from './common_utils';
/** /**
* Polling utility for handling realtime updates. * Polling utility for handling realtime updates.
* Service for vue resouce and method need to be provided as props * Requirements: Promise based HTTP client
*
* Service for promise based http client and method need to be provided as props
* *
* @example * @example
* new Poll({ * new Poll({
......
<script> <script>
import Icon from '../../vue_shared/components/icon.vue';
export default { export default {
computed: { component: {
lockIcon() { Icon,
return gl.utils.spriteIcon('lock');
},
}, },
}; };
</script> </script>
<template> <template>
<div class="disabled-comment text-center"> <div class="disabled-comment text-center">
<span class="issuable-note-warning"> <span class="issuable-note-warning inline">
<span class="icon" v-html="lockIcon"></span> <icon
name="lock"
:size="16"
class="icon">
</icon>
<span>This issue is locked. Only <b>project members</b> can comment.</span> <span>This issue is locked. Only <b>project members</b> can comment.</span>
</span> </span>
</div> </div>
......
<script> <script>
import Flash from '../../../flash'; import Flash from '../../../flash';
import editForm from './edit_form.vue'; import editForm from './edit_form.vue';
import Icon from '../../../vue_shared/components/icon.vue';
export default { export default {
components: { components: {
editForm, editForm,
Icon,
}, },
props: { props: {
isConfidential: { isConfidential: {
...@@ -26,11 +28,8 @@ export default { ...@@ -26,11 +28,8 @@ export default {
}; };
}, },
computed: { computed: {
faEye() { confidentialityIcon() {
const eye = this.isConfidential ? 'fa-eye-slash' : 'fa-eye'; return this.isConfidential ? 'eye-slash' : 'eye';
return {
[eye]: true,
};
}, },
}, },
methods: { methods: {
...@@ -49,7 +48,11 @@ export default { ...@@ -49,7 +48,11 @@ export default {
<template> <template>
<div class="block issuable-sidebar-item confidentiality"> <div class="block issuable-sidebar-item confidentiality">
<div class="sidebar-collapsed-icon"> <div class="sidebar-collapsed-icon">
<i class="fa" :class="faEye" aria-hidden="true"></i> <icon
:name="confidentialityIcon"
:size="16"
aria-hidden="true">
</icon>
</div> </div>
<div class="title hide-collapsed"> <div class="title hide-collapsed">
Confidentiality Confidentiality
...@@ -70,11 +73,21 @@ export default { ...@@ -70,11 +73,21 @@ export default {
:update-confidential-attribute="updateConfidentialAttribute" :update-confidential-attribute="updateConfidentialAttribute"
/> />
<div v-if="!isConfidential" class="no-value sidebar-item-value"> <div v-if="!isConfidential" class="no-value sidebar-item-value">
<i class="fa fa-eye sidebar-item-icon"></i> <icon
name="eye"
:size="16"
aria-hidden="true"
class="sidebar-item-icon inline">
</icon>
Not confidential Not confidential
</div> </div>
<div v-else class="value sidebar-item-value hide-collapsed"> <div v-else class="value sidebar-item-value hide-collapsed">
<i aria-hidden="true" class="fa fa-eye-slash sidebar-item-icon is-active"></i> <icon
name="eye-slash"
:size="16"
aria-hidden="true"
class="sidebar-item-icon inline is-active">
</icon>
This issue is confidential This issue is confidential
</div> </div>
</div> </div>
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* global Flash */ /* global Flash */
import editForm from './edit_form.vue'; import editForm from './edit_form.vue';
import issuableMixin from '../../../vue_shared/mixins/issuable'; import issuableMixin from '../../../vue_shared/mixins/issuable';
import Icon from '../../../vue_shared/components/icon.vue';
export default { export default {
props: { props: {
...@@ -35,11 +36,12 @@ export default { ...@@ -35,11 +36,12 @@ export default {
components: { components: {
editForm, editForm,
Icon,
}, },
computed: { computed: {
lockIconClass() { lockIcon() {
return this.isLocked ? 'fa-lock' : 'fa-unlock'; return this.isLocked ? 'lock' : 'lock-open';
}, },
isLockDialogOpen() { isLockDialogOpen() {
...@@ -66,11 +68,12 @@ export default { ...@@ -66,11 +68,12 @@ export default {
<template> <template>
<div class="block issuable-sidebar-item lock"> <div class="block issuable-sidebar-item lock">
<div class="sidebar-collapsed-icon"> <div class="sidebar-collapsed-icon">
<i <icon
class="fa" :name="lockIcon"
:class="lockIconClass" :size="16"
aria-hidden="true" aria-hidden="true"
></i> class="sidebar-item-icon is-active">
</icon>
</div> </div>
<div class="title hide-collapsed"> <div class="title hide-collapsed">
...@@ -98,10 +101,12 @@ export default { ...@@ -98,10 +101,12 @@ export default {
v-if="isLocked" v-if="isLocked"
class="value sidebar-item-value" class="value sidebar-item-value"
> >
<i <icon
name="lock"
:size="16"
aria-hidden="true" aria-hidden="true"
class="fa fa-lock sidebar-item-icon is-active" class="sidebar-item-icon inline is-active">
></i> </icon>
{{ __('Locked') }} {{ __('Locked') }}
</div> </div>
...@@ -109,10 +114,12 @@ export default { ...@@ -109,10 +114,12 @@ export default {
v-else v-else
class="no-value sidebar-item-value hide-collapsed" class="no-value sidebar-item-value hide-collapsed"
> >
<i <icon
name="lock-open"
:size="16"
aria-hidden="true" aria-hidden="true"
class="fa fa-unlock sidebar-item-icon" class="sidebar-item-icon inline">
></i> </icon>
{{ __('Unlocked') }} {{ __('Unlocked') }}
</div> </div>
</div> </div>
......
<script> <script>
import Icon from '../../../vue_shared/components/icon.vue';
export default { export default {
props: { props: {
isLocked: { isLocked: {
...@@ -14,12 +16,16 @@ ...@@ -14,12 +16,16 @@
}, },
}, },
components: {
Icon,
},
computed: { computed: {
iconClass() { warningIcon() {
return { if (this.isConfidential) return 'eye-slash';
'fa-eye-slash': this.isConfidential, if (this.isLocked) return 'lock';
'fa-lock': this.isLocked,
}; return '';
}, },
isLockedAndConfidential() { isLockedAndConfidential() {
...@@ -30,12 +36,13 @@ ...@@ -30,12 +36,13 @@
</script> </script>
<template> <template>
<div class="issuable-note-warning"> <div class="issuable-note-warning">
<i <icon
aria-hidden="true" :name="warningIcon"
class="fa icon" :size="16"
:class="iconClass" class="icon inline"
v-if="!isLockedAndConfidential" aria-hidden="true"
></i> v-if="!isLockedAndConfidential">
</icon>
<span v-if="isLockedAndConfidential"> <span v-if="isLockedAndConfidential">
{{ __('This issue is confidential and locked.') }} {{ __('This issue is confidential and locked.') }}
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
.cgray { color: $common-gray; } .cgray { color: $common-gray; }
.clgray { color: $common-gray-light; } .clgray { color: $common-gray-light; }
.cred { color: $common-red; } .cred { color: $common-red; }
svg.cred { fill: $common-red; }
.cgreen { color: $common-green; } .cgreen { color: $common-green; }
svg.cgreen { fill: $common-green; }
.cdark { color: $common-gray-dark; } .cdark { color: $common-gray-dark; }
.text-secondary { .text-secondary {
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
font-size: 12px; font-size: 12px;
border-radius: 0; border-radius: 0;
border: 0; border: 0;
padding: $grid-size;
.bash { .bash {
display: block; display: block;
...@@ -57,14 +58,13 @@ ...@@ -57,14 +58,13 @@
.top-bar { .top-bar {
height: 35px; height: 35px;
display: flex;
justify-content: flex-end;
background: $gray-light; background: $gray-light;
border: 1px solid $border-color; border: 1px solid $border-color;
color: $gl-text-color; color: $gl-text-color;
position: sticky; position: sticky;
position: -webkit-sticky; position: -webkit-sticky;
top: $header-height; top: $header-height;
padding: $grid-size;
&.affix { &.affix {
top: $header-height; top: $header-height;
...@@ -90,9 +90,6 @@ ...@@ -90,9 +90,6 @@
} }
.truncated-info { .truncated-info {
margin: 0 auto;
align-self: center;
.truncated-info-size { .truncated-info-size {
margin: 0 5px; margin: 0 5px;
} }
...@@ -118,7 +115,11 @@ ...@@ -118,7 +115,11 @@
.controllers-buttons { .controllers-buttons {
color: $gl-text-color; color: $gl-text-color;
margin: 0 10px; margin: 0 $grid-size;
&:last-child {
margin-right: 0;
}
} }
.btn-scroll.animate { .btn-scroll.animate {
......
...@@ -628,21 +628,46 @@ ...@@ -628,21 +628,46 @@
} }
.diff-file-changes { .diff-file-changes {
width: 450px; max-width: 560px;
width: 100%;
z-index: 150; z-index: 150;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
left: $gl-padding; left: $gl-padding;
} }
a { .diff-changed-file {
display: flex;
padding-top: 8px; padding-top: 8px;
padding-bottom: 8px; padding-bottom: 8px;
min-width: 0;
} }
.diff-changed-file { .diff-file-changed-icon {
margin-top: 2px;
}
.diff-changed-file-content {
display: flex; display: flex;
align-items: center; flex-direction: column;
min-width: 0;
}
.diff-changed-file-name,
.diff-changed-file-path {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.diff-changed-file-path {
direction: rtl;
color: $gl-text-color-tertiary;
}
.diff-changed-stats {
margin-left: auto;
white-space: nowrap;
} }
} }
......
...@@ -6,28 +6,20 @@ ...@@ -6,28 +6,20 @@
} }
.issuable-warning-icon { .issuable-warning-icon {
color: $orange-600;
background-color: $orange-100; background-color: $orange-100;
border-radius: $border-radius-default; border-radius: $border-radius-default;
padding: 5px;
margin: 0 $btn-side-margin 0 0; margin: 0 $btn-side-margin 0 0;
width: $issuable-warning-size; width: $issuable-warning-size;
height: $issuable-warning-size; height: $issuable-warning-size;
text-align: center; text-align: center;
&:first-of-type { .icon {
margin-right: $issuable-warning-icon-margin; fill: $orange-600;
vertical-align: text-bottom;
} }
}
.sidebar-item-icon { &:first-of-type {
border-radius: $border-radius-default; margin-right: $issuable-warning-icon-margin;
padding: 5px;
margin: 0 3px 0 -4px;
&.is-active {
color: $orange-600;
background-color: $orange-50;
} }
} }
......
...@@ -113,6 +113,8 @@ ...@@ -113,6 +113,8 @@
.icon { .icon {
margin-right: $issuable-warning-icon-margin; margin-right: $issuable-warning-icon-margin;
vertical-align: text-bottom;
fill: $orange-600;
} }
+ .md-area { + .md-area {
...@@ -137,12 +139,24 @@ ...@@ -137,12 +139,24 @@
} }
} }
.sidebar-item-value { .sidebar-item-icon {
.fa { border-radius: $border-radius-default;
background-color: inherit; margin: 0 3px 0 -4px;
vertical-align: middle;
&.is-active {
fill: $orange-600;
} }
} }
.sidebar-collapsed-icon .sidebar-item-icon {
margin: 0;
}
.sidebar-item-value .sidebar-item-icon {
fill: $theme-gray-700;
}
.sidebar-item-warning-message { .sidebar-item-warning-message {
line-height: 1.5; line-height: 1.5;
padding: 16px; padding: 16px;
......
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
color: $white-normal; color: $white-normal;
} }
&:hover { &:hover:not(.tree-truncated-warning) {
td { td {
background-color: $row-hover; background-color: $row-hover;
border-top: 1px solid $row-hover-border; border-top: 1px solid $row-hover-border;
...@@ -198,6 +198,11 @@ ...@@ -198,6 +198,11 @@
} }
} }
.tree-truncated-warning {
color: $orange-600;
background-color: $orange-100;
}
.tree-time-ago { .tree-time-ago {
min-width: 135px; min-width: 135px;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
......
...@@ -4,7 +4,7 @@ module NotesActions ...@@ -4,7 +4,7 @@ module NotesActions
included do included do
before_action :set_polling_interval_header, only: [:index] before_action :set_polling_interval_header, only: [:index]
before_action :noteable, only: :index before_action :require_noteable!, only: [:index, :create]
before_action :authorize_admin_note!, only: [:update, :destroy] before_action :authorize_admin_note!, only: [:update, :destroy]
before_action :note_project, only: [:create] before_action :note_project, only: [:create]
end end
...@@ -90,7 +90,7 @@ module NotesActions ...@@ -90,7 +90,7 @@ module NotesActions
if note.persisted? if note.persisted?
attrs[:valid] = true attrs[:valid] = true
if noteable.nil? || noteable.discussions_rendered_on_frontend? if noteable.discussions_rendered_on_frontend?
attrs.merge!(note_serializer.represent(note)) attrs.merge!(note_serializer.represent(note))
else else
attrs.merge!( attrs.merge!(
...@@ -191,7 +191,11 @@ module NotesActions ...@@ -191,7 +191,11 @@ module NotesActions
end end
def noteable def noteable
@noteable ||= notes_finder.target || render_404 @noteable ||= notes_finder.target || @note&.noteable
end
def require_noteable!
render_404 unless noteable
end end
def last_fetched_at def last_fetched_at
......
...@@ -57,6 +57,7 @@ class Projects::CommitsController < Projects::ApplicationController ...@@ -57,6 +57,7 @@ class Projects::CommitsController < Projects::ApplicationController
@repository.commits(@ref, path: @path, limit: @limit, offset: @offset) @repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
end end
@commits = @commits.with_pipeline_status
@commits = prepare_commits_for_rendering(@commits) @commits = prepare_commits_for_rendering(@commits)
end end
end end
...@@ -82,7 +82,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -82,7 +82,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def commits def commits
# Get commits from repository # Get commits from repository
# or from cache if already merged # or from cache if already merged
@commits = prepare_commits_for_rendering(@merge_request.commits) @commits =
prepare_commits_for_rendering(@merge_request.commits.with_pipeline_status)
render json: { html: view_to_html_string('projects/merge_requests/_commits') } render json: { html: view_to_html_string('projects/merge_requests/_commits') }
end end
......
...@@ -20,6 +20,7 @@ class Snippets::NotesController < ApplicationController ...@@ -20,6 +20,7 @@ class Snippets::NotesController < ApplicationController
def snippet def snippet
PersonalSnippet.find_by(id: params[:snippet_id]) PersonalSnippet.find_by(id: params[:snippet_id])
end end
alias_method :noteable, :snippet
def note_params def note_params
super.merge(noteable_id: params[:snippet_id]) super.merge(noteable_id: params[:snippet_id])
......
...@@ -30,4 +30,11 @@ module AppearancesHelper ...@@ -30,4 +30,11 @@ module AppearancesHelper
render 'shared/logo.svg' render 'shared/logo.svg'
end end
end end
# Skip the 'GitLab' type logo when custom brand logo is set
def brand_header_logo_type
unless brand_item && brand_item.header_logo?
render 'shared/logo_type.svg'
end
end
end end
...@@ -6,11 +6,6 @@ ...@@ -6,11 +6,6 @@
# See 'detailed_status?` method and `Gitlab::Ci::Status` module. # See 'detailed_status?` method and `Gitlab::Ci::Status` module.
# #
module CiStatusHelper module CiStatusHelper
def ci_status_path(pipeline)
project = pipeline.project
project_pipeline_path(project, pipeline)
end
def ci_label_for_status(status) def ci_label_for_status(status)
if detailed_status?(status) if detailed_status?(status)
return status.label return status.label
......
...@@ -153,11 +153,11 @@ module DiffHelper ...@@ -153,11 +153,11 @@ module DiffHelper
def diff_file_changed_icon(diff_file) def diff_file_changed_icon(diff_file)
if diff_file.deleted_file? || diff_file.renamed_file? if diff_file.deleted_file? || diff_file.renamed_file?
"minus" "file-deletion"
elsif diff_file.new_file? elsif diff_file.new_file?
"plus" "file-addition"
else else
"adjust" "file-modified"
end end
end end
......
module TreeHelper module TreeHelper
FILE_LIMIT = 1_000
# Sorts a repository's tree so that folders are before files and renders # Sorts a repository's tree so that folders are before files and renders
# their corresponding partials # their corresponding partials
# #
# contents - A Grit::Tree object for the current tree # tree - A `Tree` object for the current tree
def render_tree(tree) def render_tree(tree)
# Sort submodules and folders together by name ahead of files # Sort submodules and folders together by name ahead of files
folders, files, submodules = tree.trees, tree.blobs, tree.submodules folders, files, submodules = tree.trees, tree.blobs, tree.submodules
tree = "" tree = ''
items = (folders + submodules).sort_by(&:name) + files items = (folders + submodules).sort_by(&:name) + files
tree << render(partial: "projects/tree/tree_row", collection: items) if items.present?
if items.size > FILE_LIMIT
tree << render(partial: 'projects/tree/truncated_notice_tree_row',
locals: { limit: FILE_LIMIT, total: items.size })
items = items.take(FILE_LIMIT)
end
tree << render(partial: 'projects/tree/tree_row', collection: items) if items.present?
tree.html_safe tree.html_safe
end end
......
...@@ -162,34 +162,70 @@ module Ci ...@@ -162,34 +162,70 @@ module Ci
end end
end end
# ref can't be HEAD or SHA, can only be branch/tag name
scope :latest, ->(ref = nil) do
max_id = unscope(:select)
.select("max(#{quoted_table_name}.id)")
.group(:ref, :sha)
if ref
where(ref: ref, id: max_id.where(ref: ref))
else
where(id: max_id)
end
end
scope :internal, -> { where(source: internal_sources) } scope :internal, -> { where(source: internal_sources) }
# Returns the pipelines in descending order (= newest first), optionally
# limited to a number of references.
#
# ref - The name (or names) of the branch(es)/tag(s) to limit the list of
# pipelines to.
def self.newest_first(ref = nil)
relation = order(id: :desc)
ref ? relation.where(ref: ref) : relation
end
def self.latest_status(ref = nil) def self.latest_status(ref = nil)
latest(ref).status newest_first(ref).pluck(:status).first
end end
def self.latest_successful_for(ref) def self.latest_successful_for(ref)
success.latest(ref).order(id: :desc).first newest_first(ref).success.take
end end
def self.latest_successful_for_refs(refs) def self.latest_successful_for_refs(refs)
success.latest(refs).order(id: :desc).each_with_object({}) do |pipeline, hash| relation = newest_first(refs).success
relation.each_with_object({}) do |pipeline, hash|
hash[pipeline.ref] ||= pipeline hash[pipeline.ref] ||= pipeline
end end
end end
# Returns a Hash containing the latest pipeline status for every given
# commit.
#
# The keys of this Hash are the commit SHAs, the values the statuses.
#
# commits - The list of commit SHAs to get the status for.
# ref - The ref to scope the data to (e.g. "master"). If the ref is not
# given we simply get the latest status for the commits, regardless
# of what refs their pipelines belong to.
def self.latest_status_per_commit(commits, ref = nil)
p1 = arel_table
p2 = arel_table.alias
# This LEFT JOIN will filter out all but the newest row for every
# combination of (project_id, sha) or (project_id, sha, ref) if a ref is
# given.
cond = p1[:sha].eq(p2[:sha])
.and(p1[:project_id].eq(p2[:project_id]))
.and(p1[:id].lt(p2[:id]))
cond = cond.and(p1[:ref].eq(p2[:ref])) if ref
join = p1.join(p2, Arel::Nodes::OuterJoin).on(cond)
relation = select(:sha, :status)
.where(sha: commits)
.where(p2[:id].eq(nil))
.joins(join.join_sources)
relation = relation.where(ref: ref) if ref
relation.each_with_object({}) do |row, hash|
hash[row[:sha]] = row[:status]
end
end
def self.truncate_sha(sha) def self.truncate_sha(sha)
sha[0...8] sha[0...8]
end end
......
...@@ -80,6 +80,7 @@ class Commit ...@@ -80,6 +80,7 @@ class Commit
@raw = raw_commit @raw = raw_commit
@project = project @project = project
@statuses = {}
end end
def id def id
...@@ -236,11 +237,13 @@ class Commit ...@@ -236,11 +237,13 @@ class Commit
end end
def status(ref = nil) def status(ref = nil)
@statuses ||= {}
return @statuses[ref] if @statuses.key?(ref) return @statuses[ref] if @statuses.key?(ref)
@statuses[ref] = pipelines.latest_status(ref) @statuses[ref] = project.pipelines.latest_status_per_commit(id, ref)[id]
end
def set_status_for_ref(ref, status)
@statuses[ref] = status
end end
def signature def signature
......
# frozen_string_literal: true
# A collection of Commit instances for a specific project and Git reference.
class CommitCollection
include Enumerable
attr_reader :project, :ref, :commits
# project - The project the commits belong to.
# commits - The Commit instances to store.
# ref - The name of the ref (e.g. "master").
def initialize(project, commits, ref = nil)
@project = project
@commits = commits
@ref = ref
end
def each(&block)
commits.each(&block)
end
# Sets the pipeline status for every commit.
#
# Setting this status ahead of time removes the need for running a query for
# every commit we're displaying.
def with_pipeline_status
statuses = project.pipelines.latest_status_per_commit(map(&:id), ref)
each do |commit|
commit.set_status_for_ref(ref, statuses[commit.id])
end
self
end
def respond_to_missing?(message, inc_private = false)
commits.respond_to?(message, inc_private)
end
# rubocop:disable GitlabSecurity/PublicSend
def method_missing(message, *args, &block)
commits.public_send(message, *args, &block)
end
end
...@@ -265,7 +265,7 @@ module Issuable ...@@ -265,7 +265,7 @@ module Issuable
participants(user).include?(user) participants(user).include?(user)
end end
def to_hook_data(user, old_labels: [], old_assignees: []) def to_hook_data(user, old_labels: [], old_assignees: [], old_total_time_spent: nil)
changes = previous_changes changes = previous_changes
if old_labels != labels if old_labels != labels
...@@ -280,6 +280,10 @@ module Issuable ...@@ -280,6 +280,10 @@ module Issuable
end end
end end
if self.respond_to?(:total_time_spent) && old_total_time_spent != total_time_spent
changes[:total_time_spent] = [old_total_time_spent, total_time_spent]
end
Gitlab::HookData::IssuableBuilder.new(self).build(user: user, changes: changes) Gitlab::HookData::IssuableBuilder.new(self).build(user: user, changes: changes)
end end
......
...@@ -30,8 +30,10 @@ class Key < ActiveRecord::Base ...@@ -30,8 +30,10 @@ class Key < ActiveRecord::Base
after_commit :add_to_shell, on: :create after_commit :add_to_shell, on: :create
after_create :post_create_hook after_create :post_create_hook
after_create :refresh_user_cache
after_commit :remove_from_shell, on: :destroy after_commit :remove_from_shell, on: :destroy
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
after_destroy :refresh_user_cache
def key=(value) def key=(value)
value&.delete!("\n\r") value&.delete!("\n\r")
...@@ -79,6 +81,12 @@ class Key < ActiveRecord::Base ...@@ -79,6 +81,12 @@ class Key < ActiveRecord::Base
) )
end end
def refresh_user_cache
return unless user
Users::KeysCountService.new(user).refresh_cache
end
def post_destroy_hook def post_destroy_hook
SystemHooksService.new.execute_hooks_for(self, :destroy) SystemHooksService.new.execute_hooks_for(self, :destroy)
end end
......
...@@ -284,8 +284,10 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -284,8 +284,10 @@ class MergeRequestDiff < ActiveRecord::Base
def load_commits def load_commits
commits = st_commits.presence || merge_request_diff_commits commits = st_commits.presence || merge_request_diff_commits
commits = commits.map { |commit| Commit.from_hash(commit.to_hash, project) }
commits.map { |commit| Commit.from_hash(commit.to_hash, project) } CommitCollection
.new(merge_request.source_project, commits, merge_request.source_branch)
end end
def save_diffs def save_diffs
......
...@@ -260,7 +260,7 @@ class Milestone < ActiveRecord::Base ...@@ -260,7 +260,7 @@ class Milestone < ActiveRecord::Base
def start_date_should_be_less_than_due_date def start_date_should_be_less_than_due_date
if due_date <= start_date if due_date <= start_date
errors.add(:start_date, "Can't be greater than due date") errors.add(:due_date, "must be greater than start date")
end end
end end
......
...@@ -25,7 +25,7 @@ class ProjectWiki ...@@ -25,7 +25,7 @@ class ProjectWiki
end end
delegate :empty?, to: :pages delegate :empty?, to: :pages
delegate :repository_storage_path, to: :project delegate :repository_storage_path, :hashed_storage?, to: :project
def path def path
@project.path + '.wiki' @project.path + '.wiki'
......
...@@ -140,7 +140,8 @@ class Repository ...@@ -140,7 +140,8 @@ class Repository
commits = Gitlab::Git::Commit.where(options) commits = Gitlab::Git::Commit.where(options)
commits = Commit.decorate(commits, @project) if commits.present? commits = Commit.decorate(commits, @project) if commits.present?
commits
CommitCollection.new(project, commits, ref)
end end
def commits_between(from, to) def commits_between(from, to)
...@@ -156,11 +157,14 @@ class Repository ...@@ -156,11 +157,14 @@ class Repository
end end
raw_repository.gitaly_migrate(:commits_by_message) do |is_enabled| raw_repository.gitaly_migrate(:commits_by_message) do |is_enabled|
if is_enabled commits =
find_commits_by_message_by_gitaly(query, ref, path, limit, offset) if is_enabled
else find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
find_commits_by_message_by_shelling_out(query, ref, path, limit, offset) else
end find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
end
CommitCollection.new(project, commits, ref)
end end
end end
...@@ -1048,10 +1052,6 @@ class Repository ...@@ -1048,10 +1052,6 @@ class Repository
raw_repository.ls_files(actual_ref) raw_repository.ls_files(actual_ref)
end end
def gitattribute(path, name)
raw_repository.attributes(path)[name]
end
def copy_gitattributes(ref) def copy_gitattributes(ref)
actual_ref = ref || root_ref actual_ref = ref || root_ref
begin begin
......
...@@ -173,6 +173,7 @@ class User < ActiveRecord::Base ...@@ -173,6 +173,7 @@ class User < ActiveRecord::Base
after_save :ensure_namespace_correct after_save :ensure_namespace_correct
after_update :username_changed_hook, if: :username_changed? after_update :username_changed_hook, if: :username_changed?
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
after_destroy :remove_key_cache
after_commit :update_emails_with_primary_email, on: :update, if: -> { previous_changes.key?('email') } after_commit :update_emails_with_primary_email, on: :update, if: -> { previous_changes.key?('email') }
after_commit :update_invalid_gpg_signatures, on: :update, if: -> { previous_changes.key?('email') } after_commit :update_invalid_gpg_signatures, on: :update, if: -> { previous_changes.key?('email') }
...@@ -642,7 +643,9 @@ class User < ActiveRecord::Base ...@@ -642,7 +643,9 @@ class User < ActiveRecord::Base
end end
def require_ssh_key? def require_ssh_key?
keys.count == 0 && Gitlab::ProtocolAccess.allowed?('ssh') count = Users::KeysCountService.new(self).count
count.zero? && Gitlab::ProtocolAccess.allowed?('ssh')
end end
def require_password_creation? def require_password_creation?
...@@ -904,6 +907,10 @@ class User < ActiveRecord::Base ...@@ -904,6 +907,10 @@ class User < ActiveRecord::Base
system_hook_service.execute_hooks_for(self, :destroy) system_hook_service.execute_hooks_for(self, :destroy)
end end
def remove_key_cache
Users::KeysCountService.new(self).delete_cache
end
def delete_async(deleted_by:, params: {}) def delete_async(deleted_by:, params: {})
block if params[:hard_delete] block if params[:hard_delete]
DeleteUserWorker.perform_async(deleted_by.id, id, params) DeleteUserWorker.perform_async(deleted_by.id, id, params)
......
# Base class for services that count a single resource such as the number of
# issues for a project.
class BaseCountService
def relation_for_count
raise(
NotImplementedError,
'"relation_for_count" must be implemented and return an ActiveRecord::Relation'
)
end
def count
Rails.cache.fetch(cache_key, raw: raw?) { uncached_count }.to_i
end
def refresh_cache
Rails.cache.write(cache_key, uncached_count, raw: raw?)
end
def uncached_count
relation_for_count.count
end
def delete_cache
Rails.cache.delete(cache_key)
end
def raw?
false
end
def cache_key
raise NotImplementedError, 'cache_key must be implemented and return a String'
end
end
...@@ -165,7 +165,7 @@ class IssuableBaseService < BaseService ...@@ -165,7 +165,7 @@ class IssuableBaseService < BaseService
# To be overridden by subclasses # To be overridden by subclasses
end end
def update(issuable) def update(issuable) # rubocop:disable Metrics/AbcSize
change_state(issuable) change_state(issuable)
change_subscription(issuable) change_subscription(issuable)
change_todo(issuable) change_todo(issuable)
...@@ -174,6 +174,7 @@ class IssuableBaseService < BaseService ...@@ -174,6 +174,7 @@ class IssuableBaseService < BaseService
old_labels = issuable.labels.to_a old_labels = issuable.labels.to_a
old_mentioned_users = issuable.mentioned_users.to_a old_mentioned_users = issuable.mentioned_users.to_a
old_assignees = issuable.assignees.to_a old_assignees = issuable.assignees.to_a
old_total_time_spent = issuable.total_time_spent if issuable.respond_to?(:total_time_spent)
label_ids = process_label_ids(params, existing_label_ids: issuable.label_ids) label_ids = process_label_ids(params, existing_label_ids: issuable.label_ids)
params[:label_ids] = label_ids if labels_changing?(issuable.label_ids, label_ids) params[:label_ids] = label_ids if labels_changing?(issuable.label_ids, label_ids)
...@@ -210,7 +211,12 @@ class IssuableBaseService < BaseService ...@@ -210,7 +211,12 @@ class IssuableBaseService < BaseService
invalidate_cache_counts(issuable, users: affected_assignees.compact) invalidate_cache_counts(issuable, users: affected_assignees.compact)
after_update(issuable) after_update(issuable)
issuable.create_new_cross_references!(current_user) issuable.create_new_cross_references!(current_user)
execute_hooks(issuable, 'update', old_labels: old_labels, old_assignees: old_assignees) execute_hooks(
issuable,
'update',
old_labels: old_labels,
old_assignees: old_assignees,
old_total_time_spent: old_total_time_spent)
issuable.update_project_counter_caches if update_project_counters issuable.update_project_counter_caches if update_project_counters
end end
......
module Issues module Issues
class BaseService < ::IssuableBaseService class BaseService < ::IssuableBaseService
def hook_data(issue, action, old_labels: [], old_assignees: []) def hook_data(issue, action, old_labels: [], old_assignees: [], old_total_time_spent: nil)
hook_data = issue.to_hook_data(current_user, old_labels: old_labels, old_assignees: old_assignees) hook_data = issue.to_hook_data(current_user, old_labels: old_labels, old_assignees: old_assignees, old_total_time_spent: old_total_time_spent)
hook_data[:object_attributes][:action] = action hook_data[:object_attributes][:action] = action
hook_data hook_data
...@@ -22,8 +22,8 @@ module Issues ...@@ -22,8 +22,8 @@ module Issues
issue, issue.project, current_user, old_assignees) issue, issue.project, current_user, old_assignees)
end end
def execute_hooks(issue, action = 'open', old_labels: [], old_assignees: []) def execute_hooks(issue, action = 'open', old_labels: [], old_assignees: [], old_total_time_spent: nil)
issue_data = hook_data(issue, action, old_labels: old_labels, old_assignees: old_assignees) issue_data = hook_data(issue, action, old_labels: old_labels, old_assignees: old_assignees, old_total_time_spent: old_total_time_spent)
hooks_scope = issue.confidential? ? :confidential_issue_hooks : :issue_hooks hooks_scope = issue.confidential? ? :confidential_issue_hooks : :issue_hooks
issue.project.execute_hooks(issue_data, hooks_scope) issue.project.execute_hooks(issue_data, hooks_scope)
issue.project.execute_services(issue_data, hooks_scope) issue.project.execute_services(issue_data, hooks_scope)
......
...@@ -20,8 +20,8 @@ module MergeRequests ...@@ -20,8 +20,8 @@ module MergeRequests
super if changed_title super if changed_title
end end
def hook_data(merge_request, action, old_rev: nil, old_labels: [], old_assignees: []) def hook_data(merge_request, action, old_rev: nil, old_labels: [], old_assignees: [], old_total_time_spent: nil)
hook_data = merge_request.to_hook_data(current_user, old_labels: old_labels, old_assignees: old_assignees) hook_data = merge_request.to_hook_data(current_user, old_labels: old_labels, old_assignees: old_assignees, old_total_time_spent: old_total_time_spent)
hook_data[:object_attributes][:action] = action hook_data[:object_attributes][:action] = action
if old_rev && !Gitlab::Git.blank_ref?(old_rev) if old_rev && !Gitlab::Git.blank_ref?(old_rev)
hook_data[:object_attributes][:oldrev] = old_rev hook_data[:object_attributes][:oldrev] = old_rev
...@@ -30,9 +30,9 @@ module MergeRequests ...@@ -30,9 +30,9 @@ module MergeRequests
hook_data hook_data
end end
def execute_hooks(merge_request, action = 'open', old_rev: nil, old_labels: [], old_assignees: []) def execute_hooks(merge_request, action = 'open', old_rev: nil, old_labels: [], old_assignees: [], old_total_time_spent: nil)
if merge_request.project if merge_request.project
merge_data = hook_data(merge_request, action, old_rev: old_rev, old_labels: old_labels, old_assignees: old_assignees) merge_data = hook_data(merge_request, action, old_rev: old_rev, old_labels: old_labels, old_assignees: old_assignees, old_total_time_spent: old_total_time_spent)
merge_request.project.execute_hooks(merge_data, :merge_request_hooks) merge_request.project.execute_hooks(merge_data, :merge_request_hooks)
merge_request.project.execute_services(merge_data, :merge_request_hooks) merge_request.project.execute_services(merge_data, :merge_request_hooks)
end end
......
module Projects module Projects
# Base class for the various service classes that count project data (e.g. # Base class for the various service classes that count project data (e.g.
# issues or forks). # issues or forks).
class CountService class CountService < BaseCountService
# The version of the cache format. This should be bumped whenever the # The version of the cache format. This should be bumped whenever the
# underlying logic changes. This removes the need for explicitly flushing # underlying logic changes. This removes the need for explicitly flushing
# all caches. # all caches.
...@@ -11,29 +11,6 @@ module Projects ...@@ -11,29 +11,6 @@ module Projects
@project = project @project = project
end end
def relation_for_count
raise(
NotImplementedError,
'"relation_for_count" must be implemented and return an ActiveRecord::Relation'
)
end
def count
Rails.cache.fetch(cache_key) { uncached_count }
end
def refresh_cache
Rails.cache.write(cache_key, uncached_count)
end
def uncached_count
relation_for_count.count
end
def delete_cache
Rails.cache.delete(cache_key)
end
def cache_key_name def cache_key_name
raise( raise(
NotImplementedError, NotImplementedError,
......
module Projects module Projects
# Service class for getting and caching the number of forks of a project. # Service class for getting and caching the number of forks of a project.
class ForksCountService < CountService class ForksCountService < Projects::CountService
def relation_for_count def relation_for_count
@project.forks @project.forks
end end
......
module Projects module Projects
# Service class for counting and caching the number of open issues of a # Service class for counting and caching the number of open issues of a
# project. # project.
class OpenIssuesCountService < CountService class OpenIssuesCountService < Projects::CountService
def relation_for_count def relation_for_count
# We don't include confidential issues in this number since this would # We don't include confidential issues in this number since this would
# expose the number of confidential issues to non project members. # expose the number of confidential issues to non project members.
......
module Projects module Projects
# Service class for counting and caching the number of open merge requests of # Service class for counting and caching the number of open merge requests of
# a project. # a project.
class OpenMergeRequestsCountService < CountService class OpenMergeRequestsCountService < Projects::CountService
def relation_for_count def relation_for_count
@project.merge_requests.opened @project.merge_requests.opened
end end
......
...@@ -62,21 +62,14 @@ module Projects ...@@ -62,21 +62,14 @@ module Projects
# Notifications # Notifications
project.send_move_instructions(@old_path) project.send_move_instructions(@old_path)
# Move main repository # Directories on disk
# TODO: check storage type and NOOP when not using Legacy move_project_folders(project)
unless move_repo_folder(@old_path, @new_path)
raise TransferError.new('Cannot move project')
end
# Move wiki repo also if present
# TODO: check storage type and NOOP when not using Legacy
move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki")
# Move missing group labels to project # Move missing group labels to project
Labels::TransferService.new(current_user, @old_group, project).execute Labels::TransferService.new(current_user, @old_group, project).execute
# Move uploads # Move uploads
Gitlab::UploadsTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path) move_project_uploads(project)
# Move pages # Move pages
Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path) Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path)
...@@ -133,5 +126,30 @@ module Projects ...@@ -133,5 +126,30 @@ module Projects
def execute_system_hooks def execute_system_hooks
SystemHooksService.new.execute_hooks_for(project, :transfer) SystemHooksService.new.execute_hooks_for(project, :transfer)
end end
def move_project_folders(project)
return if project.hashed_storage?(:repository)
# Move main repository
unless move_repo_folder(@old_path, @new_path)
raise TransferError.new("Cannot move project")
end
# Disk path is changed; we need to ensure we reload it
project.reload_repository!
# Move wiki repo also if present
move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki")
end
def move_project_uploads(project)
return if project.hashed_storage?(:attachments)
Gitlab::UploadsTransfer.new.move_project(
project.path,
@old_namespace.full_path,
@new_namespace.full_path
)
end
end end
end end
# frozen_string_literal: true
module Users
# Service class for getting the number of SSH keys that belong to a user.
class KeysCountService < BaseCountService
attr_reader :user
# user - The User for which to get the number of SSH keys.
def initialize(user)
@user = user
end
def relation_for_count
user.keys
end
def raw?
# Since we're storing simple integers we don't need all of the additional
# Marshal data Rails includes by default.
true
end
def cache_key
"users/key-count-service/#{user.id}"
end
end
end
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
= f.hidden_field :header_logo_cache = f.hidden_field :header_logo_cache
= f.file_field :header_logo, class: "" = f.file_field :header_logo, class: ""
.hint .hint
Maximum file size is 1MB. Pages are optimized for a 72x72 px header logo Maximum file size is 1MB. Pages are optimized for a 28px tall header logo
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-save append-right-10' = f.submit 'Save', class: 'btn btn-save append-right-10'
......
...@@ -99,7 +99,7 @@ ...@@ -99,7 +99,7 @@
%td.build-link %td.build-link
- if project - if project
= link_to ci_status_path(build.pipeline) do = link_to pipeline_path(build.pipeline) do
%strong= build.pipeline.short_sha %strong= build.pipeline.short_sha
%td.timestamp %td.timestamp
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= link_to root_path, title: 'Dashboard', id: 'logo' do = link_to root_path, title: 'Dashboard', id: 'logo' do
= brand_header_logo = brand_header_logo
%span.logo-text.hidden-xs %span.logo-text.hidden-xs
= render 'shared/logo_type.svg' = brand_header_logo_type
- if current_user - if current_user
= render "layouts/nav/dashboard" = render "layouts/nav/dashboard"
......
- if commit.has_signature? - if commit.has_signature?
%a{ href: '#', tabindex: 0, class: commit_signature_badge_classes('js-loading-gpg-badge'), data: { toggle: 'tooltip', placement: 'auto top', title: 'GPG signature (loading...)', 'commit-sha' => commit.sha } } %a{ href: 'javascript:void(0)', tabindex: 0, class: commit_signature_badge_classes('js-loading-gpg-badge'), data: { toggle: 'tooltip', placement: 'auto top', title: 'GPG signature (loading...)', 'commit-sha' => commit.sha } }
...@@ -24,5 +24,5 @@ ...@@ -24,5 +24,5 @@
= link_to('Learn more about signing commits', help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link') = link_to('Learn more about signing commits', help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link')
%a{ href: '#', tabindex: 0, class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'auto top', title: title, content: content } } %a{ href: 'javascript:void(0)', tabindex: 0, class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'auto top', title: title, content: content } }
= label = label
...@@ -22,9 +22,11 @@ ...@@ -22,9 +22,11 @@
- diff_files.each do |diff_file| - diff_files.each do |diff_file|
%li %li
%a.diff-changed-file{ href: "##{hexdigest(diff_file.file_path)}", title: diff_file.new_path } %a.diff-changed-file{ href: "##{hexdigest(diff_file.file_path)}", title: diff_file.new_path }
= icon("#{diff_file_changed_icon(diff_file)} fw", class: "#{diff_file_changed_icon_color(diff_file)} append-right-5") = sprite_icon(diff_file_changed_icon(diff_file), size: 16, css_class: "#{diff_file_changed_icon_color(diff_file)} diff-file-changed-icon append-right-8")
%span.diff-file-changes-path.append-right-5= diff_file.new_path %span.diff-changed-file-content.append-right-8
.pull-right %strong.diff-changed-file-name= diff_file.blob.name
%span.diff-changed-file-path.prepend-top-5= diff_file.new_path
%span.diff-changed-stats
%span.cgreen< %span.cgreen<
+#{diff_file.added_lines} +#{diff_file.added_lines}
%span.cred< %span.cred<
......
...@@ -30,9 +30,9 @@ ...@@ -30,9 +30,9 @@
.issuable-meta .issuable-meta
- if @issue.confidential - if @issue.confidential
= icon('eye-slash', class: 'issuable-warning-icon') .issuable-warning-icon.inline= sprite_icon('eye-slash', size: 16, css_class: 'icon')
- if @issue.discussion_locked? - if @issue.discussion_locked?
= icon('lock', class: 'issuable-warning-icon') .issuable-warning-icon.inline= sprite_icon('lock', size: 16, css_class: 'icon')
= issuable_meta(@issue, @project, "Issue") = issuable_meta(@issue, @project, "Issue")
.issuable-actions.js-issuable-actions .issuable-actions.js-issuable-actions
......
...@@ -59,13 +59,13 @@ ...@@ -59,13 +59,13 @@
.build-trace-container.prepend-top-default .build-trace-container.prepend-top-default
.top-bar.js-top-bar .top-bar.js-top-bar
.js-truncated-info.truncated-info.hidden< .js-truncated-info.truncated-info.hidden-xs.pull-left.hidden<
Showing last Showing last
%span.js-truncated-info-size.truncated-info-size>< %span.js-truncated-info-size.truncated-info-size><
KiB of log - KiB of log -
%a.js-raw-link.raw-link{ href: raw_project_job_path(@project, @build) }>< Complete Raw %a.js-raw-link.raw-link{ href: raw_project_job_path(@project, @build) }>< Complete Raw
.controllers .controllers.pull-right
- if @build.has_trace? - if @build.has_trace?
= link_to raw_project_job_path(@project, @build), = link_to raw_project_job_path(@project, @build),
title: 'Show complete raw', title: 'Show complete raw',
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.issuable-meta .issuable-meta
- if @merge_request.discussion_locked? - if @merge_request.discussion_locked?
= icon('lock', class: 'issuable-warning-icon') .issuable-warning-icon.inline= sprite_icon('lock', size: 16, css_class: 'icon')
= issuable_meta(@merge_request, @project, "Merge request") = issuable_meta(@merge_request, @project, "Merge request")
.issuable-actions.js-issuable-actions .issuable-actions.js-issuable-actions
......
%tr.tree-truncated-warning
%td{ colspan: '3' }
= icon('exclamation-triangle fw')
%span
Too many items to show. To preserve performance only
%strong #{number_with_delimiter(limit)} of #{number_with_delimiter(total)}
items are displayed.
---
title: Performance issues when loading large number of wiki pages
merge_request: 15276
author:
type: performance
---
title: Changed validation error message on wrong milestone dates
merge_request:
author: Xurxo Méndez Pérez
type: fixed
---
title: Fix gitlab:backup rake for hashed storage based repositories
merge_request: 15400
author:
type: fixed
---
title: Hide log size for mobile screens
merge_request:
author:
type: fixed
---
title: Add total_time_spent to the `changes` hash in issuable Webhook payloads
merge_request: 15381
author:
type: changed
---
title: Fix issue where clicking a GPG verification badge would scroll to the top of
the page
merge_request: 15407
author:
type: fixed
---
title: When a custom header logo is present, don't show GitLab type logo
merge_request:
author:
type: changed
---
title: Fix crash when navigating to second page of the group dashbaord when there
are projects and groups on the first page
merge_request: 15456
author:
type: fixed
---
title: Cache the number of user SSH keys
merge_request:
author:
type: performance
---
title: Optimise getting the pipeline status of commits
merge_request:
author:
type: performance
---
title: Make sure NotesActions#noteable returns a Noteable in the update action
merge_request:
author:
type: fixed
---
title: Update container repository path reference and allow using double underscore
merge_request: 15417
author:
type: fixed
---
title: Improved diff changed files dropdown design
merge_request:
author:
type: changed
---
title: Fix hashed storage with project transfers to another namespace
merge_request:
author:
type: fixed
---
title: Truncate tree to max 1,000 items and display notice to users
merge_request:
author:
type: performance
---
title: 'Update emojis. Add :gay_pride_flag: and :speech_left:. Remove extraneous comma
in :cartwheel_tone4:'
merge_request:
author:
type: changed
...@@ -471,3 +471,35 @@ ...@@ -471,3 +471,35 @@
:why: :why:
:versions: [] :versions: []
:when: 2017-10-17 17:46:12.367554000 Z :when: 2017-10-17 17:46:12.367554000 Z
- - :license
- component-emitter
- MIT
- :who: Winnie Hellmann
:why: package.json does not specify the license (README.md does)
:versions:
- 1.1.2
:when: 2017-11-13 12:23:10.502463000 Z
- - :license
- json-schema
- BSD
- :who: Winnie Hellmann
:why: https://github.com/kriszyp/json-schema/blob/v0.2.3/package.json#L18-L19
:versions:
- 0.2.3
:when: 2017-11-16 12:52:18.286091000 Z
- - :license
- node-forge
- New BSD
- :who: Winnie Hellmann
:why: https://github.com/digitalbazaar/forge/blob/0.6.33/LICENSE
:versions:
- 0.6.33
:when: 2017-11-16 12:56:17.974767000 Z
- - :license
- sntp
- BSD
- :who: Winnie Hellmann
:why: https://github.com/hueniverse/sntp/blob/v1.0.9/package.json#L28-L29
:versions:
- 1.0.9
:when: 2017-11-16 13:02:06.765282000 Z
...@@ -4,7 +4,7 @@ require './spec/support/test_env' ...@@ -4,7 +4,7 @@ require './spec/support/test_env'
class Gitlab::Seeder::CycleAnalytics class Gitlab::Seeder::CycleAnalytics
def initialize(project, perf: false) def initialize(project, perf: false)
@project = project @project = project
@user = User.order(:id).last @user = User.admins.first
@issue_count = perf ? 1000 : 5 @issue_count = perf ? 1000 : 5
stub_git_pre_receive! stub_git_pre_receive!
end end
...@@ -77,39 +77,41 @@ class Gitlab::Seeder::CycleAnalytics ...@@ -77,39 +77,41 @@ class Gitlab::Seeder::CycleAnalytics
end end
def seed! def seed!
Sidekiq::Testing.inline! do Sidekiq::Worker.skipping_transaction_check do
issues = create_issues Sidekiq::Testing.inline! do
puts '.' issues = create_issues
puts '.'
# Stage 1
Timecop.travel 5.days.from_now # Stage 1
add_milestones_and_list_labels(issues) Timecop.travel 5.days.from_now
print '.' add_milestones_and_list_labels(issues)
print '.'
# Stage 2
Timecop.travel 5.days.from_now # Stage 2
branches = mention_in_commits(issues) Timecop.travel 5.days.from_now
print '.' branches = mention_in_commits(issues)
print '.'
# Stage 3
Timecop.travel 5.days.from_now # Stage 3
merge_requests = create_merge_requests_closing_issues(issues, branches) Timecop.travel 5.days.from_now
print '.' merge_requests = create_merge_requests_closing_issues(issues, branches)
print '.'
# Stage 4
Timecop.travel 5.days.from_now # Stage 4
run_builds(merge_requests) Timecop.travel 5.days.from_now
print '.' run_builds(merge_requests)
print '.'
# Stage 5
Timecop.travel 5.days.from_now # Stage 5
merge_merge_requests(merge_requests) Timecop.travel 5.days.from_now
print '.' merge_merge_requests(merge_requests)
print '.'
# Stage 6 / 7
Timecop.travel 5.days.from_now # Stage 6 / 7
deploy_to_production(merge_requests) Timecop.travel 5.days.from_now
print '.' deploy_to_production(merge_requests)
print '.'
end
end end
print '.' print '.'
...@@ -123,7 +125,7 @@ class Gitlab::Seeder::CycleAnalytics ...@@ -123,7 +125,7 @@ class Gitlab::Seeder::CycleAnalytics
title: "Cycle Analytics: #{FFaker::Lorem.sentence(6)}", title: "Cycle Analytics: #{FFaker::Lorem.sentence(6)}",
description: FFaker::Lorem.sentence, description: FFaker::Lorem.sentence,
state: 'opened', state: 'opened',
assignee: @project.team.users.sample assignees: [@project.team.users.sample]
} }
Issues::CreateService.new(@project, @project.team.users.sample, issue_params).execute Issues::CreateService.new(@project, @project.team.users.sample, issue_params).execute
...@@ -155,7 +157,7 @@ class Gitlab::Seeder::CycleAnalytics ...@@ -155,7 +157,7 @@ class Gitlab::Seeder::CycleAnalytics
issue.project.repository.add_branch(@user, branch_name, 'master') issue.project.repository.add_branch(@user, branch_name, 'master')
commit_sha = issue.project.repository.create_file(@user, filename, "content", message: "Commit for ##{issue.iid}", branch_name: branch_name) commit_sha = issue.project.repository.create_file(@user, filename, "content", message: "Commit for #{issue.to_reference}", branch_name: branch_name)
issue.project.repository.commit(commit_sha) issue.project.repository.commit(commit_sha)
GitPushService.new(issue.project, GitPushService.new(issue.project,
...@@ -210,6 +212,8 @@ class Gitlab::Seeder::CycleAnalytics ...@@ -210,6 +212,8 @@ class Gitlab::Seeder::CycleAnalytics
def deploy_to_production(merge_requests) def deploy_to_production(merge_requests)
merge_requests.each do |merge_request| merge_requests.each do |merge_request|
next unless merge_request.head_pipeline
Timecop.travel 12.hours.from_now Timecop.travel 12.hours.from_now
job = merge_request.head_pipeline.builds.where.not(environment: nil).last job = merge_request.head_pipeline.builds.where.not(environment: nil).last
...@@ -223,7 +227,14 @@ Gitlab::Seeder.quiet do ...@@ -223,7 +227,14 @@ Gitlab::Seeder.quiet do
flag = 'SEED_CYCLE_ANALYTICS' flag = 'SEED_CYCLE_ANALYTICS'
if ENV[flag] if ENV[flag]
Project.all.each do |project| Project.find_each do |project|
# This seed naively assumes that every project has a repository, and every
# repository has a `master` branch, which may be the case for a pristine
# GDK seed, but is almost never true for a GDK that's actually had
# development performed on it.
next unless project.repository_exists?
next unless project.repository.commit('master')
seeder = Gitlab::Seeder::CycleAnalytics.new(project) seeder = Gitlab::Seeder::CycleAnalytics.new(project)
seeder.seed! seeder.seed!
end end
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable # rubocop:disable Migration/UpdateLargeTable
class AddOnlyAllowMergeIfBuildSucceedsToProjects < ActiveRecord::Migration class AddOnlyAllowMergeIfBuildSucceedsToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction! disable_ddl_transaction!
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable # rubocop:disable Migration/UpdateLargeTable
class AddRepositoryStorageToProjects < ActiveRecord::Migration class AddRepositoryStorageToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction! disable_ddl_transaction!
......
# rubocop:disable Migration/UpdateLargeTable
# rubocop:disable Migration/UpdateColumnInBatches # rubocop:disable Migration/UpdateColumnInBatches
class SetMissingStageOnCiBuilds < ActiveRecord::Migration class SetMissingStageOnCiBuilds < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable # rubocop:disable Migration/UpdateLargeTable
class AddRequestAccessEnabledToProjects < ActiveRecord::Migration class AddRequestAccessEnabledToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction! disable_ddl_transaction!
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable # rubocop:disable Migration/UpdateLargeTable
class AddRequestAccessEnabledToGroups < ActiveRecord::Migration class AddRequestAccessEnabledToGroups < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction! disable_ddl_transaction!
......
# Migration type: online without errors (works on previous version and new one) # Migration type: online without errors (works on previous version and new one)
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable # rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/Datetime # rubocop:disable Migration/Datetime
# rubocop:disable Migration/UpdateLargeTable
class AddLdapSyncStateToGroups < ActiveRecord::Migration class AddLdapSyncStateToGroups < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction! disable_ddl_transaction!
......
# rubocop:disable Migration/UpdateLargeTable
# rubocop:disable Migration/UpdateColumnInBatches # rubocop:disable Migration/UpdateColumnInBatches
class DropAndReaddHasExternalWikiInProjects < ActiveRecord::Migration class DropAndReaddHasExternalWikiInProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html # See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab. # for more information on how to write migrations for GitLab.
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable # rubocop:disable Migration/UpdateLargeTable
class RemoveFeaturesEnabledFromProjects < ActiveRecord::Migration class RemoveFeaturesEnabledFromProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction! disable_ddl_transaction!
......
# rubocop:disable Migration/UpdateColumnInBatches # rubocop:disable Migration/UpdateColumnInBatches
# rubocop:disable Migration/UpdateLargeTable
class UpdateMirrorWhenEmptyImportUrlInProjects < ActiveRecord::Migration class UpdateMirrorWhenEmptyImportUrlInProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html # See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab. # for more information on how to write migrations for GitLab.
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable # rubocop:disable Migration/UpdateLargeTable
class RemoveProjectsPushesSinceGc < ActiveRecord::Migration class RemoveProjectsPushesSinceGc < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable # rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class AddSquashToMergeRequests < ActiveRecord::Migration class AddSquashToMergeRequests < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction! disable_ddl_transaction!
......
# rubocop:disable Migration/UpdateColumnInBatches # rubocop:disable Migration/UpdateColumnInBatches
# rubocop:disable Migration/UpdateLargeTable
class ConvertProjectsRepositorySizeLimitToBytes < ActiveRecord::Migration class ConvertProjectsRepositorySizeLimitToBytes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
......
# rubocop:disable Migration/UpdateColumnInBatches # rubocop:disable Migration/UpdateColumnInBatches
# rubocop:disable Migration/UpdateLargeTable
class ConvertNamespacesRepositorySizeLimitToBytes < ActiveRecord::Migration class ConvertNamespacesRepositorySizeLimitToBytes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# for more information on how to write migrations for GitLab. # for more information on how to write migrations for GitLab.
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable # rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class AddColumnAuditorToUsers < ActiveRecord::Migration class AddColumnAuditorToUsers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable # rubocop:disable Migration/UpdateLargeTable
class AddTwoFactorColumnsToNamespaces < ActiveRecord::Migration class AddTwoFactorColumnsToNamespaces < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
......
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.
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
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