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:
<<: *pull-cache
stage: test
script:
- bundle exec rake db:rollback STEP=120
- bundle exec rake db:rollback STEP=119
- bundle exec rake db:migrate
db:rollback-pg:
......
......@@ -355,7 +355,7 @@ group :development, :test do
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 'activerecord_sane_schema_dumper', '0.2'
......
......@@ -91,6 +91,7 @@ GEM
bindata (2.4.1)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
blankslate (2.1.2.4)
bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4)
......@@ -479,11 +480,13 @@ GEM
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
license_finder (2.1.0)
license_finder (3.1.1)
bundler
httparty
rubyzip
thor
toml (= 0.1.2)
with_env (> 1.0)
xml-simple
licensee (8.7.0)
rugged (~> 0.24)
......@@ -600,6 +603,8 @@ GEM
activerecord (>= 4.0, < 5.2)
parser (2.4.0.0)
ast (~> 2.2)
parslet (1.5.0)
blankslate (~> 2.0)
path_expander (1.0.1)
peek (1.0.1)
concurrent-ruby (>= 0.9.0)
......@@ -928,6 +933,8 @@ GEM
tilt (2.0.6)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
toml (0.1.2)
parslet (~> 1.5.0)
toml-rb (0.3.15)
citrus (~> 3.0, > 3.0)
truncato (0.7.10)
......@@ -982,6 +989,7 @@ GEM
builder
expression_parser
rinku
with_env (1.1.0)
xml-simple (1.1.5)
xpath (2.1.0)
nokogiri (~> 1.3)
......@@ -1095,7 +1103,7 @@ DEPENDENCIES
knapsack (~> 1.11.0)
kubeclient (~> 2.2.0)
letter_opener_web (~> 1.3.0)
license_finder (~> 2.1.0)
license_finder (~> 3.1)
licensee (~> 8.7.0)
lograge (~> 0.5)
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 @@
.catch(() => Flash('Unable to build Slack link.'));
},
},
mounted() {
GitlabSlackService.init();
},
};
</script>
......
import axios from 'axios';
import setAxiosCsrfToken from '../../lib/utils/axios_utils';
import axios from '../../lib/utils/axios_utils';
export default {
init() {
setAxiosCsrfToken();
},
addToSlack(url, projectId) {
return axios.get(url, {
params: {
......
import axios from 'axios';
import setAxiosCsrfToken from '../../lib/utils/axios_utils';
import axios from '../../lib/utils/axios_utils';
export default class ClusterService {
constructor(options = {}) {
setAxiosCsrfToken();
this.options = options;
this.appInstallEndpointMap = {
helm: this.options.installHelmEndpoint,
......@@ -18,7 +15,6 @@ export default class ClusterService {
}
installApplication(appId) {
const endpoint = this.appInstallEndpointMap[appId];
return axios.post(endpoint);
return axios.post(this.appInstallEndpointMap[appId]);
}
}
......@@ -367,7 +367,8 @@ import initGroupAnalytics from './init_group_analytics';
container: '.js-commit-pipeline-graph',
}).bindEvents();
initNotes();
initChangesDropdown();
const stickyBarPaddingTop = 16;
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - stickyBarPaddingTop);
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
break;
case 'projects:commit:pipelines':
......
......@@ -7,6 +7,17 @@ function isFlagEmoji(emojiUnicode) {
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
// 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
......@@ -57,9 +68,11 @@ function isPersonZwjEmoji(emojiUnicode) {
// in `isEmojiUnicodeSupported` logic
function checkFlagEmojiSupport(unicodeSupportMap, emojiUnicode) {
const isFlagResult = isFlagEmoji(emojiUnicode);
const isRainbowFlagResult = isRainbowFlagEmoji(emojiUnicode);
return (
(unicodeSupportMap.flag && isFlagResult) ||
!isFlagResult
(unicodeSupportMap.rainbowFlag && isRainbowFlagResult) ||
(!isFlagResult && !isRainbowFlagResult)
);
}
......@@ -113,6 +126,7 @@ function isEmojiUnicodeSupported(unicodeSupportMap = {}, emojiUnicode, unicodeVe
export {
isEmojiUnicodeSupported as default,
isFlagEmoji,
isRainbowFlagEmoji,
isKeycapEmoji,
isSkinToneComboEmoji,
isHorceRacingSkinToneComboEmoji,
......
import AccessorUtilities from '../../lib/utils/accessor';
const GL_EMOJI_VERSION = '0.2.0';
const unicodeSupportTestMap = {
// man, student (emojione does not have any of these yet), http://emojipedia.org/emoji-zwj-sequences/
// occupationZwj: '\u{1F468}\u{200D}\u{1F393}',
......@@ -13,6 +15,7 @@ const unicodeSupportTestMap = {
horseRacing: '\u{1F3C7}\u{1F3FF}',
// US flag, http://emojipedia.org/flags/
flag: '\u{1F1FA}\u{1F1F8}',
rainbowFlag: '\u{1F3F3}\u{1F308}',
// http://emojipedia.org/modifiers/
skinToneModifier: [
// spy_tone5
......@@ -141,23 +144,31 @@ function generateUnicodeSupportMap(testMap) {
}
export default function getUnicodeSupportMap() {
let unicodeSupportMap;
let userAgentFromCache;
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 {
unicodeSupportMap = JSON.parse(window.localStorage.getItem('gl-emoji-unicode-support-map'));
} catch (err) {
// swallow
}
if (!unicodeSupportMap || userAgentFromCache !== navigator.userAgent) {
if (
!unicodeSupportMap ||
glEmojiVersionFromCache !== GL_EMOJI_VERSION ||
userAgentFromCache !== navigator.userAgent
) {
unicodeSupportMap = generateUnicodeSupportMap(unicodeSupportTestMap);
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-unicode-support-map', JSON.stringify(unicodeSupportMap));
}
......
......@@ -29,8 +29,8 @@ export default class JobMediator {
this.poll = new Poll({
resource: this.service,
method: 'getJob',
successCallback: this.successCallback.bind(this),
errorCallback: this.errorCallback.bind(this),
successCallback: response => this.successCallback(response),
errorCallback: () => this.errorCallback(),
});
if (!Visibility.hidden()) {
......@@ -57,7 +57,7 @@ export default class JobMediator {
successCallback(response) {
this.state.isLoading = false;
return response.json().then(data => this.store.storeJob(data));
return this.store.storeJob(response.data);
}
errorCallback() {
......
import Vue from 'vue';
import VueResource from 'vue-resource';
Vue.use(VueResource);
import axios from '../../lib/utils/axios_utils';
export default class JobService {
constructor(endpoint) {
this.job = Vue.resource(endpoint);
this.job = endpoint;
}
getJob() {
return this.job.get();
return axios.get(this.job);
}
}
import axios from 'axios';
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';
/**
* 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
* new Poll({
......
<script>
import Icon from '../../vue_shared/components/icon.vue';
export default {
computed: {
lockIcon() {
return gl.utils.spriteIcon('lock');
},
component: {
Icon,
},
};
</script>
<template>
<div class="disabled-comment text-center">
<span class="issuable-note-warning">
<span class="icon" v-html="lockIcon"></span>
<span class="issuable-note-warning inline">
<icon
name="lock"
:size="16"
class="icon">
</icon>
<span>This issue is locked. Only <b>project members</b> can comment.</span>
</span>
</div>
......
<script>
import Flash from '../../../flash';
import editForm from './edit_form.vue';
import Icon from '../../../vue_shared/components/icon.vue';
export default {
components: {
editForm,
Icon,
},
props: {
isConfidential: {
......@@ -26,11 +28,8 @@ export default {
};
},
computed: {
faEye() {
const eye = this.isConfidential ? 'fa-eye-slash' : 'fa-eye';
return {
[eye]: true,
};
confidentialityIcon() {
return this.isConfidential ? 'eye-slash' : 'eye';
},
},
methods: {
......@@ -49,7 +48,11 @@ export default {
<template>
<div class="block issuable-sidebar-item confidentiality">
<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 class="title hide-collapsed">
Confidentiality
......@@ -70,11 +73,21 @@ export default {
:update-confidential-attribute="updateConfidentialAttribute"
/>
<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
</div>
<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
</div>
</div>
......
......@@ -2,6 +2,7 @@
/* global Flash */
import editForm from './edit_form.vue';
import issuableMixin from '../../../vue_shared/mixins/issuable';
import Icon from '../../../vue_shared/components/icon.vue';
export default {
props: {
......@@ -35,11 +36,12 @@ export default {
components: {
editForm,
Icon,
},
computed: {
lockIconClass() {
return this.isLocked ? 'fa-lock' : 'fa-unlock';
lockIcon() {
return this.isLocked ? 'lock' : 'lock-open';
},
isLockDialogOpen() {
......@@ -66,11 +68,12 @@ export default {
<template>
<div class="block issuable-sidebar-item lock">
<div class="sidebar-collapsed-icon">
<i
class="fa"
:class="lockIconClass"
<icon
:name="lockIcon"
:size="16"
aria-hidden="true"
></i>
class="sidebar-item-icon is-active">
</icon>
</div>
<div class="title hide-collapsed">
......@@ -98,10 +101,12 @@ export default {
v-if="isLocked"
class="value sidebar-item-value"
>
<i
<icon
name="lock"
:size="16"
aria-hidden="true"
class="fa fa-lock sidebar-item-icon is-active"
></i>
class="sidebar-item-icon inline is-active">
</icon>
{{ __('Locked') }}
</div>
......@@ -109,10 +114,12 @@ export default {
v-else
class="no-value sidebar-item-value hide-collapsed"
>
<i
<icon
name="lock-open"
:size="16"
aria-hidden="true"
class="fa fa-unlock sidebar-item-icon"
></i>
class="sidebar-item-icon inline">
</icon>
{{ __('Unlocked') }}
</div>
</div>
......
<script>
import Icon from '../../../vue_shared/components/icon.vue';
export default {
props: {
isLocked: {
......@@ -14,12 +16,16 @@
},
},
components: {
Icon,
},
computed: {
iconClass() {
return {
'fa-eye-slash': this.isConfidential,
'fa-lock': this.isLocked,
};
warningIcon() {
if (this.isConfidential) return 'eye-slash';
if (this.isLocked) return 'lock';
return '';
},
isLockedAndConfidential() {
......@@ -30,12 +36,13 @@
</script>
<template>
<div class="issuable-note-warning">
<i
aria-hidden="true"
class="fa icon"
:class="iconClass"
v-if="!isLockedAndConfidential"
></i>
<icon
:name="warningIcon"
:size="16"
class="icon inline"
aria-hidden="true"
v-if="!isLockedAndConfidential">
</icon>
<span v-if="isLockedAndConfidential">
{{ __('This issue is confidential and locked.') }}
......
......@@ -2,7 +2,9 @@
.cgray { color: $common-gray; }
.clgray { color: $common-gray-light; }
.cred { color: $common-red; }
svg.cred { fill: $common-red; }
.cgreen { color: $common-green; }
svg.cgreen { fill: $common-green; }
.cdark { color: $common-gray-dark; }
.text-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 @@
font-size: 12px;
border-radius: 0;
border: 0;
padding: $grid-size;
.bash {
display: block;
......@@ -57,14 +58,13 @@
.top-bar {
height: 35px;
display: flex;
justify-content: flex-end;
background: $gray-light;
border: 1px solid $border-color;
color: $gl-text-color;
position: sticky;
position: -webkit-sticky;
top: $header-height;
padding: $grid-size;
&.affix {
top: $header-height;
......@@ -90,9 +90,6 @@
}
.truncated-info {
margin: 0 auto;
align-self: center;
.truncated-info-size {
margin: 0 5px;
}
......@@ -118,7 +115,11 @@
.controllers-buttons {
color: $gl-text-color;
margin: 0 10px;
margin: 0 $grid-size;
&:last-child {
margin-right: 0;
}
}
.btn-scroll.animate {
......
......@@ -628,21 +628,46 @@
}
.diff-file-changes {
width: 450px;
max-width: 560px;
width: 100%;
z-index: 150;
@media (min-width: $screen-sm-min) {
left: $gl-padding;
}
a {
.diff-changed-file {
display: flex;
padding-top: 8px;
padding-bottom: 8px;
min-width: 0;
}
.diff-changed-file {
.diff-file-changed-icon {
margin-top: 2px;
}
.diff-changed-file-content {
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 @@
}
.issuable-warning-icon {
color: $orange-600;
background-color: $orange-100;
border-radius: $border-radius-default;
padding: 5px;
margin: 0 $btn-side-margin 0 0;
width: $issuable-warning-size;
height: $issuable-warning-size;
text-align: center;
&:first-of-type {
margin-right: $issuable-warning-icon-margin;
.icon {
fill: $orange-600;
vertical-align: text-bottom;
}
}
.sidebar-item-icon {
border-radius: $border-radius-default;
padding: 5px;
margin: 0 3px 0 -4px;
&.is-active {
color: $orange-600;
background-color: $orange-50;
&:first-of-type {
margin-right: $issuable-warning-icon-margin;
}
}
......
......@@ -113,6 +113,8 @@
.icon {
margin-right: $issuable-warning-icon-margin;
vertical-align: text-bottom;
fill: $orange-600;
}
+ .md-area {
......@@ -137,12 +139,24 @@
}
}
.sidebar-item-value {
.fa {
background-color: inherit;
.sidebar-item-icon {
border-radius: $border-radius-default;
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 {
line-height: 1.5;
padding: 16px;
......
......@@ -125,7 +125,7 @@
color: $white-normal;
}
&:hover {
&:hover:not(.tree-truncated-warning) {
td {
background-color: $row-hover;
border-top: 1px solid $row-hover-border;
......@@ -198,6 +198,11 @@
}
}
.tree-truncated-warning {
color: $orange-600;
background-color: $orange-100;
}
.tree-time-ago {
min-width: 135px;
color: $gl-text-color-secondary;
......
......@@ -4,7 +4,7 @@ module NotesActions
included do
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 :note_project, only: [:create]
end
......@@ -90,7 +90,7 @@ module NotesActions
if note.persisted?
attrs[:valid] = true
if noteable.nil? || noteable.discussions_rendered_on_frontend?
if noteable.discussions_rendered_on_frontend?
attrs.merge!(note_serializer.represent(note))
else
attrs.merge!(
......@@ -191,7 +191,11 @@ module NotesActions
end
def noteable
@noteable ||= notes_finder.target || render_404
@noteable ||= notes_finder.target || @note&.noteable
end
def require_noteable!
render_404 unless noteable
end
def last_fetched_at
......
......@@ -57,6 +57,7 @@ class Projects::CommitsController < Projects::ApplicationController
@repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
end
@commits = @commits.with_pipeline_status
@commits = prepare_commits_for_rendering(@commits)
end
end
......@@ -82,7 +82,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def commits
# Get commits from repository
# 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') }
end
......
......@@ -20,6 +20,7 @@ class Snippets::NotesController < ApplicationController
def snippet
PersonalSnippet.find_by(id: params[:snippet_id])
end
alias_method :noteable, :snippet
def note_params
super.merge(noteable_id: params[:snippet_id])
......
......@@ -30,4 +30,11 @@ module AppearancesHelper
render 'shared/logo.svg'
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
......@@ -6,11 +6,6 @@
# See 'detailed_status?` method and `Gitlab::Ci::Status` module.
#
module CiStatusHelper
def ci_status_path(pipeline)
project = pipeline.project
project_pipeline_path(project, pipeline)
end
def ci_label_for_status(status)
if detailed_status?(status)
return status.label
......
......@@ -153,11 +153,11 @@ module DiffHelper
def diff_file_changed_icon(diff_file)
if diff_file.deleted_file? || diff_file.renamed_file?
"minus"
"file-deletion"
elsif diff_file.new_file?
"plus"
"file-addition"
else
"adjust"
"file-modified"
end
end
......
module TreeHelper
FILE_LIMIT = 1_000
# Sorts a repository's tree so that folders are before files and renders
# their corresponding partials
#
# contents - A Grit::Tree object for the current tree
# tree - A `Tree` object for the current tree
def render_tree(tree)
# Sort submodules and folders together by name ahead of files
folders, files, submodules = tree.trees, tree.blobs, tree.submodules
tree = ""
tree = ''
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
end
......
......@@ -162,34 +162,70 @@ module Ci
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) }
# 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)
latest(ref).status
newest_first(ref).pluck(:status).first
end
def self.latest_successful_for(ref)
success.latest(ref).order(id: :desc).first
newest_first(ref).success.take
end
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
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)
sha[0...8]
end
......
......@@ -80,6 +80,7 @@ class Commit
@raw = raw_commit
@project = project
@statuses = {}
end
def id
......@@ -236,11 +237,13 @@ class Commit
end
def status(ref = nil)
@statuses ||= {}
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
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
participants(user).include?(user)
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
if old_labels != labels
......@@ -280,6 +280,10 @@ module Issuable
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)
end
......
......@@ -30,8 +30,10 @@ class Key < ActiveRecord::Base
after_commit :add_to_shell, on: :create
after_create :post_create_hook
after_create :refresh_user_cache
after_commit :remove_from_shell, on: :destroy
after_destroy :post_destroy_hook
after_destroy :refresh_user_cache
def key=(value)
value&.delete!("\n\r")
......@@ -79,6 +81,12 @@ class Key < ActiveRecord::Base
)
end
def refresh_user_cache
return unless user
Users::KeysCountService.new(user).refresh_cache
end
def post_destroy_hook
SystemHooksService.new.execute_hooks_for(self, :destroy)
end
......
......@@ -284,8 +284,10 @@ class MergeRequestDiff < ActiveRecord::Base
def load_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
def save_diffs
......
......@@ -260,7 +260,7 @@ class Milestone < ActiveRecord::Base
def start_date_should_be_less_than_due_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
......
......@@ -25,7 +25,7 @@ class ProjectWiki
end
delegate :empty?, to: :pages
delegate :repository_storage_path, to: :project
delegate :repository_storage_path, :hashed_storage?, to: :project
def path
@project.path + '.wiki'
......
......@@ -140,7 +140,8 @@ class Repository
commits = Gitlab::Git::Commit.where(options)
commits = Commit.decorate(commits, @project) if commits.present?
commits
CommitCollection.new(project, commits, ref)
end
def commits_between(from, to)
......@@ -156,11 +157,14 @@ class Repository
end
raw_repository.gitaly_migrate(:commits_by_message) do |is_enabled|
if is_enabled
find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
else
find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
end
commits =
if is_enabled
find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
else
find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
end
CommitCollection.new(project, commits, ref)
end
end
......@@ -1048,10 +1052,6 @@ class Repository
raw_repository.ls_files(actual_ref)
end
def gitattribute(path, name)
raw_repository.attributes(path)[name]
end
def copy_gitattributes(ref)
actual_ref = ref || root_ref
begin
......
......@@ -173,6 +173,7 @@ class User < ActiveRecord::Base
after_save :ensure_namespace_correct
after_update :username_changed_hook, if: :username_changed?
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_invalid_gpg_signatures, on: :update, if: -> { previous_changes.key?('email') }
......@@ -642,7 +643,9 @@ class User < ActiveRecord::Base
end
def require_ssh_key?
keys.count == 0 && Gitlab::ProtocolAccess.allowed?('ssh')
count = Users::KeysCountService.new(self).count
count.zero? && Gitlab::ProtocolAccess.allowed?('ssh')
end
def require_password_creation?
......@@ -904,6 +907,10 @@ class User < ActiveRecord::Base
system_hook_service.execute_hooks_for(self, :destroy)
end
def remove_key_cache
Users::KeysCountService.new(self).delete_cache
end
def delete_async(deleted_by:, params: {})
block if params[:hard_delete]
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
# To be overridden by subclasses
end
def update(issuable)
def update(issuable) # rubocop:disable Metrics/AbcSize
change_state(issuable)
change_subscription(issuable)
change_todo(issuable)
......@@ -174,6 +174,7 @@ class IssuableBaseService < BaseService
old_labels = issuable.labels.to_a
old_mentioned_users = issuable.mentioned_users.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)
params[:label_ids] = label_ids if labels_changing?(issuable.label_ids, label_ids)
......@@ -210,7 +211,12 @@ class IssuableBaseService < BaseService
invalidate_cache_counts(issuable, users: affected_assignees.compact)
after_update(issuable)
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
end
......
module Issues
class BaseService < ::IssuableBaseService
def hook_data(issue, action, old_labels: [], old_assignees: [])
hook_data = issue.to_hook_data(current_user, old_labels: old_labels, old_assignees: 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, old_total_time_spent: old_total_time_spent)
hook_data[:object_attributes][:action] = action
hook_data
......@@ -22,8 +22,8 @@ module Issues
issue, issue.project, current_user, old_assignees)
end
def execute_hooks(issue, action = 'open', old_labels: [], old_assignees: [])
issue_data = hook_data(issue, action, old_labels: old_labels, old_assignees: 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, old_total_time_spent: old_total_time_spent)
hooks_scope = issue.confidential? ? :confidential_issue_hooks : :issue_hooks
issue.project.execute_hooks(issue_data, hooks_scope)
issue.project.execute_services(issue_data, hooks_scope)
......
......@@ -20,8 +20,8 @@ module MergeRequests
super if changed_title
end
def hook_data(merge_request, action, old_rev: nil, old_labels: [], old_assignees: [])
hook_data = merge_request.to_hook_data(current_user, old_labels: old_labels, old_assignees: 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, old_total_time_spent: old_total_time_spent)
hook_data[:object_attributes][:action] = action
if old_rev && !Gitlab::Git.blank_ref?(old_rev)
hook_data[:object_attributes][:oldrev] = old_rev
......@@ -30,9 +30,9 @@ module MergeRequests
hook_data
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
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_services(merge_data, :merge_request_hooks)
end
......
module Projects
# Base class for the various service classes that count project data (e.g.
# issues or forks).
class CountService
class CountService < BaseCountService
# The version of the cache format. This should be bumped whenever the
# underlying logic changes. This removes the need for explicitly flushing
# all caches.
......@@ -11,29 +11,6 @@ module Projects
@project = project
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
raise(
NotImplementedError,
......
module Projects
# Service class for getting and caching the number of forks of a project.
class ForksCountService < CountService
class ForksCountService < Projects::CountService
def relation_for_count
@project.forks
end
......
module Projects
# Service class for counting and caching the number of open issues of a
# project.
class OpenIssuesCountService < CountService
class OpenIssuesCountService < Projects::CountService
def relation_for_count
# We don't include confidential issues in this number since this would
# expose the number of confidential issues to non project members.
......
module Projects
# Service class for counting and caching the number of open merge requests of
# a project.
class OpenMergeRequestsCountService < CountService
class OpenMergeRequestsCountService < Projects::CountService
def relation_for_count
@project.merge_requests.opened
end
......
......@@ -62,21 +62,14 @@ module Projects
# Notifications
project.send_move_instructions(@old_path)
# Move main repository
# TODO: check storage type and NOOP when not using Legacy
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")
# Directories on disk
move_project_folders(project)
# Move missing group labels to project
Labels::TransferService.new(current_user, @old_group, project).execute
# Move uploads
Gitlab::UploadsTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path)
move_project_uploads(project)
# Move pages
Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path)
......@@ -133,5 +126,30 @@ module Projects
def execute_system_hooks
SystemHooksService.new.execute_hooks_for(project, :transfer)
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
# 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 @@
= f.hidden_field :header_logo_cache
= f.file_field :header_logo, class: ""
.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
= f.submit 'Save', class: 'btn btn-save append-right-10'
......
......@@ -99,7 +99,7 @@
%td.build-link
- if project
= link_to ci_status_path(build.pipeline) do
= link_to pipeline_path(build.pipeline) do
%strong= build.pipeline.short_sha
%td.timestamp
......
......@@ -7,7 +7,7 @@
= link_to root_path, title: 'Dashboard', id: 'logo' do
= brand_header_logo
%span.logo-text.hidden-xs
= render 'shared/logo_type.svg'
= brand_header_logo_type
- if current_user
= render "layouts/nav/dashboard"
......
- 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 @@
= 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
......@@ -22,9 +22,11 @@
- diff_files.each do |diff_file|
%li
%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")
%span.diff-file-changes-path.append-right-5= diff_file.new_path
.pull-right
= 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-changed-file-content.append-right-8
%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<
+#{diff_file.added_lines}
%span.cred<
......
......@@ -30,9 +30,9 @@
.issuable-meta
- 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?
= icon('lock', class: 'issuable-warning-icon')
.issuable-warning-icon.inline= sprite_icon('lock', size: 16, css_class: 'icon')
= issuable_meta(@issue, @project, "Issue")
.issuable-actions.js-issuable-actions
......
......@@ -59,13 +59,13 @@
.build-trace-container.prepend-top-default
.top-bar.js-top-bar
.js-truncated-info.truncated-info.hidden<
.js-truncated-info.truncated-info.hidden-xs.pull-left.hidden<
Showing last
%span.js-truncated-info-size.truncated-info-size><
KiB of log -
%a.js-raw-link.raw-link{ href: raw_project_job_path(@project, @build) }>< Complete Raw
.controllers
.controllers.pull-right
- if @build.has_trace?
= link_to raw_project_job_path(@project, @build),
title: 'Show complete raw',
......
......@@ -16,7 +16,7 @@
.issuable-meta
- 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-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 @@
:why:
:versions: []
: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'
class Gitlab::Seeder::CycleAnalytics
def initialize(project, perf: false)
@project = project
@user = User.order(:id).last
@user = User.admins.first
@issue_count = perf ? 1000 : 5
stub_git_pre_receive!
end
......@@ -77,39 +77,41 @@ class Gitlab::Seeder::CycleAnalytics
end
def seed!
Sidekiq::Testing.inline! do
issues = create_issues
puts '.'
# Stage 1
Timecop.travel 5.days.from_now
add_milestones_and_list_labels(issues)
print '.'
# Stage 2
Timecop.travel 5.days.from_now
branches = mention_in_commits(issues)
print '.'
# Stage 3
Timecop.travel 5.days.from_now
merge_requests = create_merge_requests_closing_issues(issues, branches)
print '.'
# Stage 4
Timecop.travel 5.days.from_now
run_builds(merge_requests)
print '.'
# Stage 5
Timecop.travel 5.days.from_now
merge_merge_requests(merge_requests)
print '.'
# Stage 6 / 7
Timecop.travel 5.days.from_now
deploy_to_production(merge_requests)
print '.'
Sidekiq::Worker.skipping_transaction_check do
Sidekiq::Testing.inline! do
issues = create_issues
puts '.'
# Stage 1
Timecop.travel 5.days.from_now
add_milestones_and_list_labels(issues)
print '.'
# Stage 2
Timecop.travel 5.days.from_now
branches = mention_in_commits(issues)
print '.'
# Stage 3
Timecop.travel 5.days.from_now
merge_requests = create_merge_requests_closing_issues(issues, branches)
print '.'
# Stage 4
Timecop.travel 5.days.from_now
run_builds(merge_requests)
print '.'
# Stage 5
Timecop.travel 5.days.from_now
merge_merge_requests(merge_requests)
print '.'
# Stage 6 / 7
Timecop.travel 5.days.from_now
deploy_to_production(merge_requests)
print '.'
end
end
print '.'
......@@ -123,7 +125,7 @@ class Gitlab::Seeder::CycleAnalytics
title: "Cycle Analytics: #{FFaker::Lorem.sentence(6)}",
description: FFaker::Lorem.sentence,
state: 'opened',
assignee: @project.team.users.sample
assignees: [@project.team.users.sample]
}
Issues::CreateService.new(@project, @project.team.users.sample, issue_params).execute
......@@ -155,7 +157,7 @@ class Gitlab::Seeder::CycleAnalytics
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)
GitPushService.new(issue.project,
......@@ -210,6 +212,8 @@ class Gitlab::Seeder::CycleAnalytics
def deploy_to_production(merge_requests)
merge_requests.each do |merge_request|
next unless merge_request.head_pipeline
Timecop.travel 12.hours.from_now
job = merge_request.head_pipeline.builds.where.not(environment: nil).last
......@@ -223,7 +227,14 @@ Gitlab::Seeder.quiet do
flag = 'SEED_CYCLE_ANALYTICS'
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.seed!
end
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class AddOnlyAllowMergeIfBuildSucceedsToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class AddRepositoryStorageToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
......
# rubocop:disable Migration/UpdateLargeTable
# rubocop:disable Migration/UpdateColumnInBatches
class SetMissingStageOnCiBuilds < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class AddRequestAccessEnabledToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class AddRequestAccessEnabledToGroups < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
......
# Migration type: online without errors (works on previous version and new one)
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/Datetime
# rubocop:disable Migration/UpdateLargeTable
class AddLdapSyncStateToGroups < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
......
# rubocop:disable Migration/UpdateLargeTable
# rubocop:disable Migration/UpdateColumnInBatches
class DropAndReaddHasExternalWikiInProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class RemoveFeaturesEnabledFromProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
......
# rubocop:disable Migration/UpdateColumnInBatches
# rubocop:disable Migration/UpdateLargeTable
class UpdateMirrorWhenEmptyImportUrlInProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class RemoveProjectsPushesSinceGc < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class AddSquashToMergeRequests < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
......
# rubocop:disable Migration/UpdateColumnInBatches
# rubocop:disable Migration/UpdateLargeTable
class ConvertProjectsRepositorySizeLimitToBytes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
......
# rubocop:disable Migration/UpdateColumnInBatches
# rubocop:disable Migration/UpdateLargeTable
class ConvertNamespacesRepositorySizeLimitToBytes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
......
......@@ -2,6 +2,7 @@
# for more information on how to write migrations for GitLab.
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class AddColumnAuditorToUsers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
......
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
# rubocop:disable Migration/UpdateLargeTable
class AddTwoFactorColumnsToNamespaces < ActiveRecord::Migration
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