Commit 710422a0 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2017-08-30

* upstream/master: (103 commits)
  Fix broken Frontend JS guide
  Replace 'project/star.feature' spinach test with an rspec analog
  Adds position fixed to right sidebar
  Decrease Metrics/CyclomaticComplexity threshold to 15
  Prevents rendering empty badge when pipeline request fails
  Make file encoding dropdown consistent
  Fixes the diff changes buttons from toggling when scrolling
  exclude spec/ and features/ from `Style/PredicateName` cop
  Resolve "Monitoring graph date formatting is wrong"
  add changelog
  replace `is_ancestor` with `ancestor?`
  replace `is_member_of` with `member_of?`
  replace `has_n_stars` with `has_n_stars?`
  replace `has_matching_label` with `has_matching_label?`
  replace `is_team_member?` with `team_member?`
  replace `is_spam?` with `spam?`
  replace `is_default_branch?` with `default_branch?`
  replace `is_successful?` with `successful?`
  replace `is_multi_check?` with `multi_check?`
  replace `is_gitlab_user?` with `gitlab_user?`
  ...
parents f0deea60 499b6406
...@@ -74,19 +74,6 @@ stages: ...@@ -74,19 +74,6 @@ stages:
- docker.elastic.co/elasticsearch/elasticsearch:5.5.2 - docker.elastic.co/elasticsearch/elasticsearch:5.5.2
.only-if-want-mysql: &only-if-want-mysql
only:
- /mysql/
- /-stable/
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
- tags@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ee
- tags@gitlab/gitlabhq
- tags@gitlab/gitlab-ee
# Skip all jobs except the ones that begin with 'docs/'. # Skip all jobs except the ones that begin with 'docs/'.
# Used for commits including ONLY documentation changes. # Used for commits including ONLY documentation changes.
# https://docs.gitlab.com/ce/development/writing_documentation.html#testing # https://docs.gitlab.com/ce/development/writing_documentation.html#testing
...@@ -130,7 +117,6 @@ stages: ...@@ -130,7 +117,6 @@ stages:
.rspec-metadata-mysql: &rspec-metadata-mysql .rspec-metadata-mysql: &rspec-metadata-mysql
<<: *rspec-metadata <<: *rspec-metadata
<<: *use-mysql <<: *use-mysql
<<: *only-if-want-mysql
<<: *except-docs <<: *except-docs
.spinach-metadata: &spinach-metadata .spinach-metadata: &spinach-metadata
...@@ -162,7 +148,6 @@ stages: ...@@ -162,7 +148,6 @@ stages:
.spinach-metadata-mysql: &spinach-metadata-mysql .spinach-metadata-mysql: &spinach-metadata-mysql
<<: *spinach-metadata <<: *spinach-metadata
<<: *use-mysql <<: *use-mysql
<<: *only-if-want-mysql
<<: *except-docs <<: *except-docs
.only-canonical-masters: &only-canonical-masters .only-canonical-masters: &only-canonical-masters
......
...@@ -608,6 +608,18 @@ Style/YodaCondition: ...@@ -608,6 +608,18 @@ Style/YodaCondition:
Style/Proc: Style/Proc:
Enabled: true Enabled: true
# Use `spam?` instead of `is_spam?`
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
# NamePrefix: is_, has_, have_
# NamePrefixBlacklist: is_, has_, have_
# NameWhitelist: is_a?
Style/PredicateName:
Enabled: true
NamePrefixBlacklist: is_
Exclude:
- 'spec/**/*'
- 'features/**/*'
# Metrics ##################################################################### # Metrics #####################################################################
# A calculated magnitude based on number of assignments, # A calculated magnitude based on number of assignments,
...@@ -633,7 +645,7 @@ Metrics/ClassLength: ...@@ -633,7 +645,7 @@ Metrics/ClassLength:
# of test cases needed to validate a method. # of test cases needed to validate a method.
Metrics/CyclomaticComplexity: Metrics/CyclomaticComplexity:
Enabled: true Enabled: true
Max: 16 Max: 15
# Limit lines to 80 characters. # Limit lines to 80 characters.
Metrics/LineLength: Metrics/LineLength:
...@@ -1186,6 +1198,10 @@ GitlabSecurity/DeepMunge: ...@@ -1186,6 +1198,10 @@ GitlabSecurity/DeepMunge:
- 'lib/**/*.rake' - 'lib/**/*.rake'
- 'spec/**/*' - 'spec/**/*'
# To be enabled by https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13610
GitlabSecurity/JsonSerialization:
Enabled: false
GitlabSecurity/PublicSend: GitlabSecurity/PublicSend:
Enabled: true Enabled: true
Exclude: Exclude:
......
...@@ -237,14 +237,6 @@ Style/PercentLiteralDelimiters: ...@@ -237,14 +237,6 @@ Style/PercentLiteralDelimiters:
Style/PerlBackrefs: Style/PerlBackrefs:
Enabled: false Enabled: false
# Offense count: 105
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
# NamePrefix: is_, has_, have_
# NamePrefixBlacklist: is_, has_, have_
# NameWhitelist: is_a?
Style/PredicateName:
Enabled: false
# Offense count: 58 # Offense count: 58
# Cop supports --auto-correct. # Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles. # Configuration parameters: EnforcedStyle, SupportedStyles.
......
...@@ -349,7 +349,7 @@ group :development, :test do ...@@ -349,7 +349,7 @@ group :development, :test do
gem 'rubocop', '~> 0.49.1', require: false gem 'rubocop', '~> 0.49.1', require: false
gem 'rubocop-rspec', '~> 1.15.1', require: false gem 'rubocop-rspec', '~> 1.15.1', require: false
gem 'rubocop-gitlab-security', '~> 0.0.6', require: false gem 'rubocop-gitlab-security', '~> 0.1.0', require: false
gem 'scss_lint', '~> 0.54.0', require: false gem 'scss_lint', '~> 0.54.0', require: false
gem 'haml_lint', '~> 0.26.0', require: false gem 'haml_lint', '~> 0.26.0', require: false
gem 'simplecov', '~> 0.14.0', require: false gem 'simplecov', '~> 0.14.0', require: false
......
...@@ -798,7 +798,7 @@ GEM ...@@ -798,7 +798,7 @@ GEM
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1) unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-gitlab-security (0.0.6) rubocop-gitlab-security (0.1.0)
rubocop (>= 0.47.1) rubocop (>= 0.47.1)
rubocop-rspec (1.15.1) rubocop-rspec (1.15.1)
rubocop (>= 0.42.0) rubocop (>= 0.42.0)
...@@ -1163,7 +1163,7 @@ DEPENDENCIES ...@@ -1163,7 +1163,7 @@ DEPENDENCIES
rspec-set (~> 0.1.3) rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5) rspec_profiling (~> 0.0.5)
rubocop (~> 0.49.1) rubocop (~> 0.49.1)
rubocop-gitlab-security (~> 0.0.6) rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.15.1) rubocop-rspec (~> 1.15.1)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2) ruby-prof (~> 0.16.2)
...@@ -1213,4 +1213,4 @@ DEPENDENCIES ...@@ -1213,4 +1213,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.15.3 1.15.4
...@@ -211,13 +211,13 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -211,13 +211,13 @@ import initGroupAnalytics from './init_group_analytics';
break; break;
case 'dashboard:issues': case 'dashboard:issues':
case 'dashboard:merge_requests': case 'dashboard:merge_requests':
case 'groups:merge_requests':
new ProjectSelect(); new ProjectSelect();
initLegacyFilters(); initLegacyFilters();
break; break;
case 'groups:issues': case 'groups:issues':
case 'groups:merge_requests':
if (filteredSearchEnabled) { if (filteredSearchEnabled) {
const filteredSearchManager = new gl.FilteredSearchManager('issues'); const filteredSearchManager = new gl.FilteredSearchManager(page === 'groups:issues' ? 'issues' : 'merge_requests');
filteredSearchManager.setup(); filteredSearchManager.setup();
} }
new ProjectSelect(); new ProjectSelect();
......
export const isSticky = (el, scrollY, stickyTop) => { export const isSticky = (el, scrollY, stickyTop) => {
const top = el.offsetTop - scrollY; const top = Math.floor(el.offsetTop - scrollY);
if (top <= stickyTop) { if (top <= stickyTop) {
el.classList.add('is-stuck'); el.classList.add('is-stuck');
......
...@@ -253,6 +253,7 @@ import bp from './breakpoints'; ...@@ -253,6 +253,7 @@ import bp from './breakpoints';
loadDiff(source) { loadDiff(source) {
if (this.diffsLoaded) { if (this.diffsLoaded) {
document.dispatchEvent(new CustomEvent('scroll'));
return; return;
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import measurements from '../utils/measurements'; import measurements from '../utils/measurements';
import { formatRelevantDigits } from '../../lib/utils/number_utils'; import { formatRelevantDigits } from '../../lib/utils/number_utils';
import { timeScaleFormat } from '../utils/date_time_formatters';
import bp from '../../breakpoints'; import bp from '../../breakpoints';
const bisectDate = d3.bisector(d => d.time).left; const bisectDate = d3.bisector(d => d.time).left;
...@@ -159,6 +160,7 @@ ...@@ -159,6 +160,7 @@
const xAxis = d3.svg.axis() const xAxis = d3.svg.axis()
.scale(axisXScale) .scale(axisXScale)
.ticks(measurements.xTicks) .ticks(measurements.xTicks)
.tickFormat(timeScaleFormat)
.orient('bottom'); .orient('bottom');
const yAxis = d3.svg.axis() const yAxis = d3.svg.axis()
...@@ -266,14 +268,6 @@ ...@@ -266,14 +268,6 @@
stroke-width="2" stroke-width="2"
transform="translate(-5, 20)"> transform="translate(-5, 20)">
</path> </path>
<rect
class="prometheus-graph-overlay"
:width="(graphWidth - 70)"
:height="(graphHeight - 100)"
transform="translate(-5, 20)"
ref="graphOverlay"
@mousemove="handleMouseOverGraph($event)">
</rect>
<monitoring-deployment <monitoring-deployment
:show-deploy-info="showDeployInfo" :show-deploy-info="showDeployInfo"
:deployment-data="reducedDeploymentData" :deployment-data="reducedDeploymentData"
...@@ -289,6 +283,14 @@ ...@@ -289,6 +283,14 @@
:graph-height="graphHeight" :graph-height="graphHeight"
:graph-height-offset="graphHeightOffset" :graph-height-offset="graphHeightOffset"
/> />
<rect
class="prometheus-graph-overlay"
:width="(graphWidth - 70)"
:height="(graphHeight - 100)"
transform="translate(-5, 20)"
ref="graphOverlay"
@mousemove="handleMouseOverGraph($event)">
</rect>
</svg> </svg>
</svg> </svg>
</div> </div>
......
<script> <script>
import { import { dateFormat, timeFormat } from '../utils/date_time_formatters';
dateFormat,
timeFormat,
} from '../constants';
export default { export default {
props: { props: {
...@@ -58,7 +55,7 @@ ...@@ -58,7 +55,7 @@
class="deploy-info" class="deploy-info"
v-if="showDeployInfo"> v-if="showDeployInfo">
<g <g
v-for="(deployment, index) in deploymentData" v-for="(deployment, index) in deploymentData"
:key="index" :key="index"
:class="nameDeploymentClass(deployment)" :class="nameDeploymentClass(deployment)"
:transform="transformDeploymentGroup(deployment)"> :transform="transformDeploymentGroup(deployment)">
...@@ -92,7 +89,7 @@ ...@@ -92,7 +89,7 @@
width="90" width="90"
height="58"> height="58">
</rect> </rect>
<g <g
transform="translate(5, 2)"> transform="translate(5, 2)">
<text <text
class="deploy-info-text text-metric-bold"> class="deploy-info-text text-metric-bold">
......
<script> <script>
import { import { dateFormat, timeFormat } from '../utils/date_time_formatters';
dateFormat,
timeFormat,
} from '../constants';
export default { export default {
props: { props: {
...@@ -72,7 +69,7 @@ ...@@ -72,7 +69,7 @@
r="5" r="5"
transform="translate(-5, 20)"> transform="translate(-5, 20)">
</circle> </circle>
<svg <svg
class="rect-text-metric" class="rect-text-metric"
:x="currentFlagPosition" :x="currentFlagPosition"
y="0"> y="0">
......
import d3 from 'd3';
export const dateFormat = d3.time.format('%b %d, %Y');
export const timeFormat = d3.time.format('%H:%M%p');
import d3 from 'd3';
export const dateFormat = d3.time.format('%b %-d, %Y');
export const timeFormat = d3.time.format('%-I:%M%p');
export const timeScaleFormat = d3.time.format.multi([
['.%L', d => d.getMilliseconds()],
[':%S', d => d.getSeconds()],
['%-I:%M', d => d.getMinutes()],
['%-I %p', d => d.getHours()],
['%a %-d', d => d.getDay() && d.getDate() !== 1],
['%b %-d', d => d.getDate() !== 1],
['%B', d => d.getMonth()],
['%Y', () => true],
]);
...@@ -47,7 +47,6 @@ export default class NewNavSidebar { ...@@ -47,7 +47,6 @@ export default class NewNavSidebar {
if (this.$sidebar.length) { if (this.$sidebar.length) {
this.$sidebar.toggleClass('sidebar-icons-only', collapsed); this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
this.$page.toggleClass('page-with-new-sidebar', !collapsed);
this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed); this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
} }
NewNavSidebar.setCollapsedCookie(collapsed); NewNavSidebar.setCollapsedCookie(collapsed);
......
<script> <script>
export default { export default {
name: 'PipelineNavigationTabs', name: 'PipelineNavigationTabs',
props: { props: {
scope: { scope: {
type: String, type: String,
required: true, required: true,
},
count: {
type: Object,
required: true,
},
paths: {
type: Object,
required: true,
},
}, },
count: { mounted() {
type: Object, $(document).trigger('init.scrolling-tabs');
required: true,
}, },
paths: { methods: {
type: Object, shouldRenderBadge(count) {
required: true, // 0 is valid in a badge, but evaluates to false, we need to check for undefined
return count !== undefined;
},
}, },
},
mounted() {
$(document).trigger('init.scrolling-tabs');
},
}; };
</script> </script>
<template> <template>
...@@ -27,7 +33,9 @@ export default { ...@@ -27,7 +33,9 @@ export default {
:class="{ active: scope === 'all'}"> :class="{ active: scope === 'all'}">
<a :href="paths.allPath"> <a :href="paths.allPath">
All All
<span class="badge js-totalbuilds-count"> <span
v-if="shouldRenderBadge(count.all)"
class="badge js-totalbuilds-count">
{{count.all}} {{count.all}}
</span> </span>
</a> </a>
...@@ -37,7 +45,9 @@ export default { ...@@ -37,7 +45,9 @@ export default {
:class="{ active: scope === 'pending'}"> :class="{ active: scope === 'pending'}">
<a :href="paths.pendingPath"> <a :href="paths.pendingPath">
Pending Pending
<span class="badge"> <span
v-if="shouldRenderBadge(count.pending)"
class="badge">
{{count.pending}} {{count.pending}}
</span> </span>
</a> </a>
...@@ -47,7 +57,9 @@ export default { ...@@ -47,7 +57,9 @@ export default {
:class="{ active: scope === 'running'}"> :class="{ active: scope === 'running'}">
<a :href="paths.runningPath"> <a :href="paths.runningPath">
Running Running
<span class="badge"> <span
v-if="shouldRenderBadge(count.running)"
class="badge">
{{count.running}} {{count.running}}
</span> </span>
</a> </a>
...@@ -57,7 +69,9 @@ export default { ...@@ -57,7 +69,9 @@ export default {
:class="{ active: scope === 'finished'}"> :class="{ active: scope === 'finished'}">
<a :href="paths.finishedPath"> <a :href="paths.finishedPath">
Finished Finished
<span class="badge"> <span
v-if="shouldRenderBadge(count.finished)"
class="badge">
{{count.finished}} {{count.finished}}
</span> </span>
</a> </a>
......
...@@ -278,7 +278,9 @@ ...@@ -278,7 +278,9 @@
} }
// TODO: change global style // TODO: change global style
.ajax-project-dropdown { .ajax-project-dropdown,
body[data-page="projects:blob:new"] #select2-drop,
body[data-page="projects:blob:edit"] #select2-drop {
&.select2-drop { &.select2-drop {
color: $gl-text-color; color: $gl-text-color;
} }
......
...@@ -120,6 +120,7 @@ $gl-text-color-quaternary: #d6d6d6; ...@@ -120,6 +120,7 @@ $gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1.0); $gl-text-color-inverted: rgba(255, 255, 255, 1.0);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, .85); $gl-text-color-secondary-inverted: rgba(255, 255, 255, .85);
$gl-text-green: $green-600; $gl-text-green: $green-600;
$gl-text-green-hover: $green-700;
$gl-text-red: $red-500; $gl-text-red: $red-500;
$gl-text-orange: $orange-600; $gl-text-orange: $orange-600;
$gl-link-color: $blue-600; $gl-link-color: $blue-600;
......
...@@ -102,6 +102,8 @@ ...@@ -102,6 +102,8 @@
} }
.member-search-form { .member-search-form {
@include new-style-dropdown;
position: relative; position: relative;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
......
...@@ -475,6 +475,8 @@ ...@@ -475,6 +475,8 @@
} }
.mr-source-target { .mr-source-target {
@include new-style-dropdown;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
......
...@@ -766,17 +766,25 @@ ul.notes { ...@@ -766,17 +766,25 @@ ul.notes {
background-color: transparent; background-color: transparent;
border: none; border: none;
outline: 0; outline: 0;
transition: color $general-hover-transition-duration $general-hover-transition-curve;
&.is-disabled { &.is-disabled {
cursor: default; cursor: default;
} }
&:not(.is-disabled):hover, &:not(.is-disabled) {
&:hover,
&:focus {
color: $gl-text-green;
}
}
&.is-active { &.is-active {
color: $gl-text-green; color: $gl-text-green;
svg { &:hover,
fill: $gl-text-green; &:focus {
color: $gl-text-green-hover;
} }
} }
......
...@@ -99,6 +99,30 @@ ...@@ -99,6 +99,30 @@
.blob-viewer-container { .blob-viewer-container {
flex: 1; flex: 1;
overflow: auto; overflow: auto;
> div,
.file-content {
display: flex;
}
> div,
.file-content,
.blob-viewer,
.line-number,
.blob-content,
.code {
min-height: 100%;
width: 100%;
}
.line-numbers {
min-width: 44px;
}
.blob-content {
flex: 1;
overflow-x: auto;
}
} }
#tabs { #tabs {
......
...@@ -265,3 +265,7 @@ ...@@ -265,3 +265,7 @@
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
} }
} }
.todos-filters {
@include new-style-dropdown;
}
...@@ -35,13 +35,13 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -35,13 +35,13 @@ class Groups::MilestonesController < Groups::ApplicationController
end end
def edit def edit
render_404 if @milestone.is_legacy_group_milestone? render_404 if @milestone.legacy_group_milestone?
end end
def update def update
# Keep this compatible with legacy group milestones where we have to update # Keep this compatible with legacy group milestones where we have to update
# all projects milestones states at once. # all projects milestones states at once.
if @milestone.is_legacy_group_milestone? if @milestone.legacy_group_milestone?
update_params = milestone_params.select { |key| key == "state_event" } update_params = milestone_params.select { |key| key == "state_event" }
milestones = @milestone.milestones milestones = @milestone.milestones
else else
...@@ -67,7 +67,7 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -67,7 +67,7 @@ class Groups::MilestonesController < Groups::ApplicationController
end end
def milestone_path def milestone_path
if @milestone.is_legacy_group_milestone? if @milestone.legacy_group_milestone?
group_milestone_path(group, @milestone.safe_title, title: @milestone.title) group_milestone_path(group, @milestone.safe_title, title: @milestone.title)
else else
group_milestone_path(group, @milestone.iid) group_milestone_path(group, @milestone.iid)
......
...@@ -205,7 +205,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -205,7 +205,7 @@ class Projects::IssuesController < Projects::ApplicationController
task_status: @issue.task_status task_status: @issue.task_status
} }
if @issue.is_edited? if @issue.edited?
response[:updated_at] = @issue.updated_at response[:updated_at] = @issue.updated_at
response[:updated_by_name] = @issue.last_edited_by.name response[:updated_by_name] = @issue.last_edited_by.name
response[:updated_by_path] = user_path(@issue.last_edited_by) response[:updated_by_path] = user_path(@issue.last_edited_by)
......
...@@ -327,14 +327,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -327,14 +327,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
elsif @merge_request.head_pipeline.success? elsif @merge_request.head_pipeline.success?
# This can be triggered when a user clicks the auto merge button while # This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time # the tests finish at about the same time
MergeWorker.perform_async(@merge_request.id, current_user.id, params) @merge_request.merge_async(current_user.id, params)
:success :success
else else
:failed :failed
end end
else else
MergeWorker.perform_async(@merge_request.id, current_user.id, params) @merge_request.merge_async(current_user.id, params)
:success :success
end end
......
...@@ -181,7 +181,7 @@ module ApplicationHelper ...@@ -181,7 +181,7 @@ module ApplicationHelper
end end
def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', exclude_author: false) def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', exclude_author: false)
return unless object.is_edited? return unless object.edited?
content_tag :small, class: 'edited-text' do content_tag :small, class: 'edited-text' do
output = content_tag(:span, 'Edited ') output = content_tag(:span, 'Edited ')
......
...@@ -237,7 +237,7 @@ module IssuablesHelper ...@@ -237,7 +237,7 @@ module IssuablesHelper
end end
def updated_at_by(issuable) def updated_at_by(issuable)
return {} unless issuable.is_edited? return {} unless issuable.edited?
{ {
updatedAt: issuable.updated_at.to_time.iso8601, updatedAt: issuable.updated_at.to_time.iso8601,
......
...@@ -196,7 +196,7 @@ module MilestonesHelper ...@@ -196,7 +196,7 @@ module MilestonesHelper
def group_milestone_route(milestone, params = {}) def group_milestone_route(milestone, params = {})
params = nil if params.empty? params = nil if params.empty?
if milestone.is_legacy_group_milestone? if milestone.legacy_group_milestone?
group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: params) group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: params)
else else
group_milestone_path(@group, milestone.iid, milestone: params) group_milestone_path(@group, milestone.iid, milestone: params)
......
module MilestonesRoutingHelper module MilestonesRoutingHelper
def milestone_path(milestone, *args) def milestone_path(milestone, *args)
if milestone.is_group_milestone? if milestone.group_milestone?
group_milestone_path(milestone.group, milestone, *args) group_milestone_path(milestone.group, milestone, *args)
elsif milestone.is_project_milestone? elsif milestone.project_milestone?
project_milestone_path(milestone.project, milestone, *args) project_milestone_path(milestone.project, milestone, *args)
end end
end end
def milestone_url(milestone, *args) def milestone_url(milestone, *args)
if milestone.is_group_milestone? if milestone.group_milestone?
group_milestone_url(milestone.group, milestone, *args) group_milestone_url(milestone.group, milestone, *args)
elsif milestone.is_project_milestone? elsif milestone.project_milestone?
project_milestone_url(milestone.project, milestone, *args) project_milestone_url(milestone.project, milestone, *args)
end end
end end
......
...@@ -143,7 +143,7 @@ module Ci ...@@ -143,7 +143,7 @@ module Ci
expire: RUNNER_QUEUE_EXPIRY_TIME, overwrite: false) expire: RUNNER_QUEUE_EXPIRY_TIME, overwrite: false)
end end
def is_runner_queue_value_latest?(value) def runner_queue_value_latest?(value)
ensure_runner_queue_value == value if value.present? ensure_runner_queue_value == value if value.present?
end end
......
module Editable module Editable
extend ActiveSupport::Concern extend ActiveSupport::Concern
def is_edited? def edited?
last_edited_at.present? && last_edited_at != created_at last_edited_at.present? && last_edited_at != created_at
end end
......
...@@ -70,19 +70,19 @@ module Milestoneish ...@@ -70,19 +70,19 @@ module Milestoneish
due_date && due_date.past? due_date && due_date.past?
end end
def is_group_milestone? def group_milestone?
false false
end end
def is_project_milestone? def project_milestone?
false false
end end
def is_legacy_group_milestone? def legacy_group_milestone?
false false
end end
def is_dashboard_milestone? def dashboard_milestone?
false false
end end
......
...@@ -23,7 +23,7 @@ module ProtectedRef ...@@ -23,7 +23,7 @@ module ProtectedRef
# If we don't `protected_branch` or `protected_tag` would be empty and # If we don't `protected_branch` or `protected_tag` would be empty and
# `project` cannot be delegated to it, which in turn would cause validations # `project` cannot be delegated to it, which in turn would cause validations
# to fail. # to fail.
has_many :"#{type}_access_levels", dependent: :destroy, inverse_of: self.model_name.singular # rubocop:disable Cop/ActiveRecordDependent has_many :"#{type}_access_levels", inverse_of: self.model_name.singular # rubocop:disable Cop/ActiveRecordDependent
validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." } validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." }
......
...@@ -3,7 +3,7 @@ class DashboardMilestone < GlobalMilestone ...@@ -3,7 +3,7 @@ class DashboardMilestone < GlobalMilestone
{ authorized_only: true } { authorized_only: true }
end end
def is_dashboard_milestone? def dashboard_milestone?
true true
end end
end end
...@@ -49,7 +49,7 @@ class Deployment < ActiveRecord::Base ...@@ -49,7 +49,7 @@ class Deployment < ActiveRecord::Base
# created before then could have a `sha` referring to a commit that no # created before then could have a `sha` referring to a commit that no
# longer exists in the repository, so just ignore those. # longer exists in the repository, so just ignore those.
begin begin
project.repository.is_ancestor?(commit.id, sha) project.repository.ancestor?(commit.id, sha)
rescue Rugged::OdbError rescue Rugged::OdbError
false false
end end
......
...@@ -248,13 +248,17 @@ class Group < Namespace ...@@ -248,13 +248,17 @@ class Group < Namespace
SystemHooksService.new SystemHooksService.new
end end
<<<<<<< HEAD
def first_non_empty_project def first_non_empty_project
projects.detect { |project| !project.empty_repo? } projects.detect { |project| !project.empty_repo? }
end end
def refresh_members_authorized_projects def refresh_members_authorized_projects
=======
def refresh_members_authorized_projects(blocking: true)
>>>>>>> upstream/master
UserProjectAccessChangedService.new(user_ids_for_project_authorizations) UserProjectAccessChangedService.new(user_ids_for_project_authorizations)
.execute .execute(blocking: blocking)
end end
def user_ids_for_project_authorizations def user_ids_for_project_authorizations
......
...@@ -19,7 +19,7 @@ class GroupMilestone < GlobalMilestone ...@@ -19,7 +19,7 @@ class GroupMilestone < GlobalMilestone
{ group_id: group.id } { group_id: group.id }
end end
def is_legacy_group_milestone? def legacy_group_milestone?
true true
end end
end end
...@@ -249,6 +249,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -249,6 +249,14 @@ class MergeRequest < ActiveRecord::Base
end end
end end
# Calls `MergeWorker` to proceed with the merge process and
# updates `merge_jid` with the MergeWorker#jid.
# This helps tracking enqueued and ongoing merge jobs.
def merge_async(user_id, params)
jid = MergeWorker.perform_async(id, user_id, params)
update_column(:merge_jid, jid)
end
def first_commit def first_commit
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end end
...@@ -392,9 +400,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -392,9 +400,7 @@ class MergeRequest < ActiveRecord::Base
end end
def merge_ongoing? def merge_ongoing?
return false unless merge_jid !!merge_jid && !merged?
Gitlab::SidekiqStatus.num_running([merge_jid]) > 0
end end
def closed_without_fork? def closed_without_fork?
...@@ -844,7 +850,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -844,7 +850,7 @@ class MergeRequest < ActiveRecord::Base
lock_mr lock_mr
yield yield
ensure ensure
unlock_mr if locked? unlock_mr
end end
end end
......
...@@ -167,7 +167,7 @@ class Milestone < ActiveRecord::Base ...@@ -167,7 +167,7 @@ class Milestone < ActiveRecord::Base
# Milestone.first.to_reference(same_namespace_project) # => "gitlab-ce%1" # Milestone.first.to_reference(same_namespace_project) # => "gitlab-ce%1"
# #
def to_reference(from_project = nil, format: :iid, full: false) def to_reference(from_project = nil, format: :iid, full: false)
return if is_group_milestone? && format != :name return if group_milestone? && format != :name
format_reference = milestone_format_reference(format) format_reference = milestone_format_reference(format)
reference = "#{self.class.reference_prefix}#{format_reference}" reference = "#{self.class.reference_prefix}#{format_reference}"
...@@ -211,11 +211,11 @@ class Milestone < ActiveRecord::Base ...@@ -211,11 +211,11 @@ class Milestone < ActiveRecord::Base
group || project group || project
end end
def is_group_milestone? def group_milestone?
group_id.present? group_id.present?
end end
def is_project_milestone? def project_milestone?
project_id.present? project_id.present?
end end
......
...@@ -152,14 +152,14 @@ module Network ...@@ -152,14 +152,14 @@ module Network
end end
def find_free_parent_space(range, space_base, space_step, space_default) def find_free_parent_space(range, space_base, space_step, space_default)
if is_overlap?(range, space_default) if overlap?(range, space_default)
find_free_space(range, space_step, space_base, space_default) find_free_space(range, space_step, space_base, space_default)
else else
space_default space_default
end end
end end
def is_overlap?(range, overlap_space) def overlap?(range, overlap_space)
range.each do |i| range.each do |i|
if i != range.first && if i != range.first &&
i != range.last && i != range.last &&
......
...@@ -27,46 +27,45 @@ class NotificationRecipient ...@@ -27,46 +27,45 @@ class NotificationRecipient
@notification_setting ||= find_notification_setting @notification_setting ||= find_notification_setting
end end
def raw_notification_level
notification_setting&.level&.to_sym
end
def notification_level def notification_level
# custom is treated the same as watch if it's enabled - otherwise it's @notification_level ||= notification_setting&.level&.to_sym
# set to :custom, meaning to send exactly when our type is :participating
# or :mention.
@notification_level ||=
case raw_notification_level
when :custom
if @custom_action && notification_setting&.event_enabled?(@custom_action)
:watch
else
:custom
end
else
raw_notification_level
end
end end
def notifiable? def notifiable?
return false unless has_access? return false unless has_access?
return false if own_activity? return false if own_activity?
return true if @type == :subscription # even users with :disabled notifications receive manual subscriptions
return !unsubscribed? if @type == :subscription
return false if notification_level.nil? || notification_level == :disabled
return %i[participating mention].include?(@type) if notification_level == :custom
return false if %i[watch participating].include?(notification_level) && excluded_watcher_action? return false unless suitable_notification_level?
return false unless NotificationSetting.levels[notification_level] <= NotificationSetting.levels[@type]
# check this last because it's expensive
# nobody should receive notifications if they've specifically unsubscribed
return false if unsubscribed? return false if unsubscribed?
true true
end end
def suitable_notification_level?
case notification_level
when :disabled, nil
false
when :custom
custom_enabled? || %i[participating mention].include?(@type)
when :watch, :participating
!excluded_watcher_action?
when :mention
@type == :mention
else
false
end
end
def custom_enabled?
@custom_action && notification_setting&.event_enabled?(@custom_action)
end
def unsubscribed? def unsubscribed?
return false unless @target return false unless @target
return false unless @target.respond_to?(:subscriptions) return false unless @target.respond_to?(:subscriptions)
...@@ -98,7 +97,7 @@ class NotificationRecipient ...@@ -98,7 +97,7 @@ class NotificationRecipient
def excluded_watcher_action? def excluded_watcher_action?
return false unless @custom_action return false unless @custom_action
return false if raw_notification_level == :custom return false if notification_level == :custom
NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(@custom_action) NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(@custom_action)
end end
......
...@@ -101,9 +101,9 @@ class ChatNotificationService < Service ...@@ -101,9 +101,9 @@ class ChatNotificationService < Service
when "push", "tag_push" when "push", "tag_push"
ChatMessage::PushMessage.new(data) ChatMessage::PushMessage.new(data)
when "issue" when "issue"
ChatMessage::IssueMessage.new(data) unless is_update?(data) ChatMessage::IssueMessage.new(data) unless update?(data)
when "merge_request" when "merge_request"
ChatMessage::MergeMessage.new(data) unless is_update?(data) ChatMessage::MergeMessage.new(data) unless update?(data)
when "note" when "note"
ChatMessage::NoteMessage.new(data) ChatMessage::NoteMessage.new(data)
when "pipeline" when "pipeline"
...@@ -136,7 +136,7 @@ class ChatNotificationService < Service ...@@ -136,7 +136,7 @@ class ChatNotificationService < Service
project.web_url project.web_url
end end
def is_update?(data) def update?(data)
data[:object_attributes][:action] == 'update' data[:object_attributes][:action] == 'update'
end end
......
...@@ -85,9 +85,9 @@ class HipchatService < Service ...@@ -85,9 +85,9 @@ class HipchatService < Service
when "push", "tag_push" when "push", "tag_push"
create_push_message(data) create_push_message(data)
when "issue" when "issue"
create_issue_message(data) unless is_update?(data) create_issue_message(data) unless update?(data)
when "merge_request" when "merge_request"
create_merge_request_message(data) unless is_update?(data) create_merge_request_message(data) unless update?(data)
when "note" when "note"
create_note_message(data) create_note_message(data)
when "pipeline" when "pipeline"
...@@ -283,7 +283,7 @@ class HipchatService < Service ...@@ -283,7 +283,7 @@ class HipchatService < Service
"<a href=\"#{project_url}\">#{project_name}</a>" "<a href=\"#{project_url}\">#{project_name}</a>"
end end
def is_update?(data) def update?(data)
data[:object_attributes][:action] == 'update' data[:object_attributes][:action] == 'update'
end end
......
...@@ -970,7 +970,7 @@ class Repository ...@@ -970,7 +970,7 @@ class Repository
if branch_commit if branch_commit
same_head = branch_commit.id == root_ref_commit.id same_head = branch_commit.id == root_ref_commit.id
!same_head && is_ancestor?(branch_commit.id, root_ref_commit.id) !same_head && ancestor?(branch_commit.id, root_ref_commit.id)
else else
nil nil
end end
...@@ -1032,12 +1032,12 @@ class Repository ...@@ -1032,12 +1032,12 @@ class Repository
nil nil
end end
def is_ancestor?(ancestor_id, descendant_id) def ancestor?(ancestor_id, descendant_id)
return false if ancestor_id.nil? || descendant_id.nil? return false if ancestor_id.nil? || descendant_id.nil?
Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled| Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
if is_enabled if is_enabled
raw_repository.is_ancestor?(ancestor_id, descendant_id) raw_repository.ancestor?(ancestor_id, descendant_id)
else else
rugged_is_ancestor?(ancestor_id, descendant_id) rugged_is_ancestor?(ancestor_id, descendant_id)
end end
......
...@@ -19,13 +19,13 @@ class ProjectPolicy < BasePolicy ...@@ -19,13 +19,13 @@ class ProjectPolicy < BasePolicy
desc "Project has public builds enabled" desc "Project has public builds enabled"
condition(:public_builds, scope: :subject) { project.public_builds? } condition(:public_builds, scope: :subject) { project.public_builds? }
# For guest access we use #is_team_member? so we can use # For guest access we use #team_member? so we can use
# project.members, which gets cached in subject scope. # project.members, which gets cached in subject scope.
# This is safe because team_access_level is guaranteed # This is safe because team_access_level is guaranteed
# by ProjectAuthorization's validation to be at minimum # by ProjectAuthorization's validation to be at minimum
# GUEST # GUEST
desc "User has guest access" desc "User has guest access"
condition(:guest) { is_team_member? } condition(:guest) { team_member? }
desc "User has reporter access" desc "User has reporter access"
condition(:reporter) { team_access_level >= Gitlab::Access::REPORTER } condition(:reporter) { team_access_level >= Gitlab::Access::REPORTER }
...@@ -296,7 +296,7 @@ class ProjectPolicy < BasePolicy ...@@ -296,7 +296,7 @@ class ProjectPolicy < BasePolicy
private private
def is_team_member? def team_member?
return false if @user.nil? return false if @user.nil?
greedy_load_subject = false greedy_load_subject = false
......
...@@ -7,7 +7,7 @@ class AkismetService ...@@ -7,7 +7,7 @@ class AkismetService
@options = options @options = options
end end
def is_spam? def spam?
return false unless akismet_enabled? return false unless akismet_enabled?
params = { params = {
......
...@@ -15,7 +15,7 @@ module Ci ...@@ -15,7 +15,7 @@ module Ci
pipeline_schedule: schedule pipeline_schedule: schedule
) )
result = validate(current_user || trigger_request.trigger.owner, result = validate(current_user,
ignore_skip_ci: ignore_skip_ci, ignore_skip_ci: ignore_skip_ci,
save_on_errors: save_on_errors, save_on_errors: save_on_errors,
mirror_update: mirror_update) mirror_update: mirror_update)
......
...@@ -30,7 +30,7 @@ class GitPushService < BaseService ...@@ -30,7 +30,7 @@ class GitPushService < BaseService
@project.repository.after_create_branch @project.repository.after_create_branch
# Re-find the pushed commits. # Re-find the pushed commits.
if is_default_branch? if default_branch?
# Initial push to the default branch. Take the full history of that branch as "newly pushed". # Initial push to the default branch. Take the full history of that branch as "newly pushed".
process_default_branch process_default_branch
else else
...@@ -50,7 +50,7 @@ class GitPushService < BaseService ...@@ -50,7 +50,7 @@ class GitPushService < BaseService
# Update the bare repositories info/attributes file using the contents of the default branches # Update the bare repositories info/attributes file using the contents of the default branches
# .gitattributes file # .gitattributes file
update_gitattributes if is_default_branch? update_gitattributes if default_branch?
end end
if current_application_settings.elasticsearch_indexing? && is_default_branch? if current_application_settings.elasticsearch_indexing? && is_default_branch?
...@@ -71,7 +71,7 @@ class GitPushService < BaseService ...@@ -71,7 +71,7 @@ class GitPushService < BaseService
end end
def update_caches def update_caches
if is_default_branch? if default_branch?
if push_to_new_branch? if push_to_new_branch?
# If this is the initial push into the default branch, the file type caches # If this is the initial push into the default branch, the file type caches
# will already be reset as a result of `Project#change_head`. # will already be reset as a result of `Project#change_head`.
...@@ -113,7 +113,7 @@ class GitPushService < BaseService ...@@ -113,7 +113,7 @@ class GitPushService < BaseService
# Schedules processing of commit messages. # Schedules processing of commit messages.
def process_commit_messages def process_commit_messages
default = is_default_branch? default = default_branch?
@push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit| @push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit|
if commit.matches_cross_reference_regex? if commit.matches_cross_reference_regex?
...@@ -216,7 +216,7 @@ class GitPushService < BaseService ...@@ -216,7 +216,7 @@ class GitPushService < BaseService
Gitlab::Git.branch_ref?(params[:ref]) Gitlab::Git.branch_ref?(params[:ref])
end end
def is_default_branch? def default_branch?
Gitlab::Git.branch_ref?(params[:ref]) && Gitlab::Git.branch_ref?(params[:ref]) &&
(Gitlab::Git.ref_name(params[:ref]) == project.default_branch || project.default_branch.nil?) (Gitlab::Git.ref_name(params[:ref]) == project.default_branch || project.default_branch.nil?)
end end
......
...@@ -33,10 +33,12 @@ module MergeRequests ...@@ -33,10 +33,12 @@ module MergeRequests
merge_request.in_locked_state do merge_request.in_locked_state do
if commit if commit
after_merge after_merge
clean_merge_jid
success success
end end
end end
rescue MergeError => e rescue MergeError => e
clean_merge_jid
log_merge_error(e.message, save_message_on_model: true) log_merge_error(e.message, save_message_on_model: true)
end end
...@@ -99,6 +101,10 @@ module MergeRequests ...@@ -99,6 +101,10 @@ module MergeRequests
end end
end end
def clean_merge_jid
merge_request.update_column(:merge_jid, nil)
end
def branch_deletion_user def branch_deletion_user
@merge_request.force_remove_source_branch? ? @merge_request.author : current_user @merge_request.force_remove_source_branch? ? @merge_request.author : current_user
end end
......
...@@ -30,7 +30,7 @@ module MergeRequests ...@@ -30,7 +30,7 @@ module MergeRequests
next next
end end
MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params) merge_request.merge_async(merge_request.merge_user_id, merge_request.merge_params)
end end
end end
......
...@@ -95,7 +95,7 @@ module MergeRequests ...@@ -95,7 +95,7 @@ module MergeRequests
if merge_request.head_pipeline && merge_request.head_pipeline.active? if merge_request.head_pipeline && merge_request.head_pipeline.active?
MergeRequests::MergeWhenPipelineSucceedsService.new(project, current_user).execute(merge_request) MergeRequests::MergeWhenPipelineSucceedsService.new(project, current_user).execute(merge_request)
else else
MergeWorker.perform_async(merge_request.id, current_user.id, {}) merge_request.merge_async(current_user.id, {})
end end
end end
......
module Milestones module Milestones
class CloseService < Milestones::BaseService class CloseService < Milestones::BaseService
def execute(milestone) def execute(milestone)
if milestone.close && milestone.is_project_milestone? if milestone.close && milestone.project_milestone?
event_service.close_milestone(milestone, current_user) event_service.close_milestone(milestone, current_user)
end end
......
...@@ -3,7 +3,7 @@ module Milestones ...@@ -3,7 +3,7 @@ module Milestones
def execute def execute
milestone = parent.milestones.new(params) milestone = parent.milestones.new(params)
if milestone.save && milestone.is_project_milestone? if milestone.save && milestone.project_milestone?
event_service.open_milestone(milestone, current_user) event_service.open_milestone(milestone, current_user)
end end
......
module Milestones module Milestones
class DestroyService < Milestones::BaseService class DestroyService < Milestones::BaseService
def execute(milestone) def execute(milestone)
return unless milestone.is_project_milestone? return unless milestone.project_milestone?
Milestone.transaction do Milestone.transaction do
update_params = { milestone: nil } update_params = { milestone: nil }
......
module Milestones module Milestones
class ReopenService < Milestones::BaseService class ReopenService < Milestones::BaseService
def execute(milestone) def execute(milestone)
if milestone.activate && milestone.is_project_milestone? if milestone.activate && milestone.project_milestone?
event_service.reopen_milestone(milestone, current_user) event_service.reopen_milestone(milestone, current_user)
end end
......
...@@ -95,7 +95,7 @@ module NotificationRecipientService ...@@ -95,7 +95,7 @@ module NotificationRecipientService
def add_participants(user) def add_participants(user)
return unless target.respond_to?(:participants) return unless target.respond_to?(:participants)
self << [target.participants(user), :watch] self << [target.participants(user), :participating]
end end
# Get project/group users with CUSTOM notification level # Get project/group users with CUSTOM notification level
......
...@@ -102,15 +102,26 @@ module Projects ...@@ -102,15 +102,26 @@ module Projects
event_service.create_project(@project, current_user) event_service.create_project(@project, current_user)
system_hook_service.execute_hooks_for(@project, :create) system_hook_service.execute_hooks_for(@project, :create)
unless @project.group || @project.gitlab_project_import? setup_authorizations
owners = [current_user, @project.namespace.owner].compact.uniq end
@project.add_master(owners, current_user: current_user)
end
<<<<<<< HEAD
# EE-only # EE-only
create_predefined_push_rule create_predefined_push_rule
@project.group&.refresh_members_authorized_projects @project.group&.refresh_members_authorized_projects
=======
# Refresh the current user's authorizations inline (so they can access the
# project immediately after this request completes), and any other affected
# users in the background
def setup_authorizations
if @project.group
@project.group.refresh_members_authorized_projects(blocking: false)
current_user.refresh_authorized_projects
else
@project.add_master(@project.namespace.owner, current_user: current_user)
end
>>>>>>> upstream/master
end end
def skip_wiki? def skip_wiki?
......
...@@ -45,7 +45,7 @@ class SpamService ...@@ -45,7 +45,7 @@ class SpamService
def check(api) def check(api)
return false unless request && check_for_spam? return false unless request && check_for_spam?
return false unless akismet.is_spam? return false unless akismet.spam?
create_spam_log(api) create_spam_log(api)
true true
......
...@@ -142,7 +142,7 @@ module SystemNoteService ...@@ -142,7 +142,7 @@ module SystemNoteService
# #
# Returns the created Note object # Returns the created Note object
def change_milestone(noteable, project, author, milestone) def change_milestone(noteable, project, author, milestone)
format = milestone&.is_group_milestone? ? :name : :iid format = milestone&.group_milestone? ? :name : :iid
body = milestone.nil? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project, format: format)}" body = milestone.nil? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project, format: format)}"
create_note(NoteSummary.new(noteable, project, author, body, action: 'milestone')) create_note(NoteSummary.new(noteable, project, author, body, action: 'milestone'))
......
...@@ -2,47 +2,16 @@ module TestHooks ...@@ -2,47 +2,16 @@ module TestHooks
class SystemService < TestHooks::BaseService class SystemService < TestHooks::BaseService
private private
def project
@project ||= begin
project = Project.first
throw(:validation_error, 'Ensure that at least one project exists.') unless project
project
end
end
def push_events_data def push_events_data
if project.empty_repo? Gitlab::DataBuilder::Push.sample_data
throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
end
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end end
def tag_push_events_data def tag_push_events_data
if project.repository.tags.empty? Gitlab::DataBuilder::Push.sample_data
throw(:validation_error, "Ensure project \"#{project.human_name}\" has tags.")
end
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end end
def repository_update_events_data def repository_update_events_data
commit = project.commit Gitlab::DataBuilder::Repository.sample_data
ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
unless commit
throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
end
change = Gitlab::DataBuilder::Repository.single_change(
commit.parent_id || Gitlab::Git::BLANK_SHA,
commit.id,
ref
)
Gitlab::DataBuilder::Repository.update(project, current_user, [change], [ref])
end end
end end
end end
...@@ -5,7 +5,13 @@ class UserProjectAccessChangedService ...@@ -5,7 +5,13 @@ class UserProjectAccessChangedService
@user_ids = Array.wrap(user_ids) @user_ids = Array.wrap(user_ids)
end end
def execute def execute(blocking: true)
AuthorizedProjectsWorker.bulk_perform_and_wait(@user_ids.map { |id| [id] }) bulk_args = @user_ids.map { |id| [id] }
if blocking
AuthorizedProjectsWorker.bulk_perform_and_wait(bulk_args)
else
AuthorizedProjectsWorker.bulk_perform_async(bulk_args)
end
end end
end end
- page_title "Merge Requests" - page_title "Merge Requests"
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'filtered_search'
- if show_new_nav? && current_user - if show_new_nav? && current_user
- content_for :breadcrumbs_extra do - content_for :breadcrumbs_extra do
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests
...@@ -13,7 +17,7 @@ ...@@ -13,7 +17,7 @@
.nav-controls{ class: ("visible-xs" if show_new_nav?) } .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests
= render 'shared/issuable/filter', type: :merge_requests = render 'shared/issuable/search_bar', type: :merge_requests
.row-content-block.second-block .row-content-block.second-block
Only merge requests from Only merge requests from
......
= render "header_title" = render "header_title"
= render 'shared/milestones/top', milestone: @milestone, group: @group = render 'shared/milestones/top', milestone: @milestone, group: @group
= render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true if @milestone.is_legacy_group_milestone? = render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true if @milestone.legacy_group_milestone?
= render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102 = render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102
- breadcrumb_link = breadcrumb_title_link - breadcrumb_link = breadcrumb_title_link
- container = @no_breadcrumb_container ? 'container-fluid' : container_class
- hide_top_links = @hide_top_links || false - hide_top_links = @hide_top_links || false
%nav.breadcrumbs{ role: "navigation" } %nav.breadcrumbs{ role: "navigation" }
.breadcrumbs-container{ class: [container_class, @content_class] } .breadcrumbs-container{ class: [container, @content_class] }
- if defined?(@new_sidebar) - if defined?(@new_sidebar)
= button_tag class: 'toggle-mobile-nav', type: 'button' do = button_tag class: 'toggle-mobile-nav', type: 'button' do
%span.sr-only Open sidebar %span.sr-only Open sidebar
......
<<<<<<< HEAD
- board = local_assigns.fetch(:board, nil) - board = local_assigns.fetch(:board, nil)
=======
- @no_breadcrumb_container = true
>>>>>>> upstream/master
- @no_container = true - @no_container = true
- @content_class = "issue-boards-content js-focus-mode-board" - @content_class = "issue-boards-content js-focus-mode-board"
- page_title "Boards" - page_title "Boards"
......
...@@ -68,9 +68,10 @@ ...@@ -68,9 +68,10 @@
- if git_import_enabled? - if git_import_enabled?
%button.btn.js-toggle-button.import_git{ type: "button" } %button.btn.js-toggle-button.import_git{ type: "button" }
= icon('git', text: 'Repo by URL') = icon('git', text: 'Repo by URL')
.import_gitlab_project.has-tooltip{ data: { container: 'body' } } - if gitlab_project_import_enabled?
= link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do .import_gitlab_project.has-tooltip{ data: { container: 'body' } }
= icon('gitlab', text: 'GitLab export') = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
= icon('gitlab', text: 'GitLab export')
.row .row
.col-lg-12 .col-lg-12
......
...@@ -26,8 +26,12 @@ ...@@ -26,8 +26,12 @@
":title" => "buttonText", ":title" => "buttonText",
":ref" => "'button'" } ":ref" => "'button'" }
= icon('spin spinner', 'v-show' => 'loading', class: 'loading', 'aria-hidden' => 'true', 'aria-label' => 'Loading') = icon('spin spinner', 'v-if' => 'loading', class: 'loading', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
%div{ 'v-show' => '!loading' }= render 'shared/icons/icon_status_success.svg' %div{ 'v-else' => '' }
%template{ 'v-if' => 'isResolved' }
= render 'shared/icons/icon_status_success_solid.svg'
%template{ 'v-else' => '' }
= render 'shared/icons/icon_status_success.svg'
- if current_user - if current_user
- if note.emoji_awardable? - if note.emoji_awardable?
......
<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z"/></g></svg> <svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill-rule="evenodd"/><path d="M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z"/></svg>
<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z" fill-rule="evenodd"/></svg>
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
.row .row
.col-sm-6 .col-sm-6
%strong= link_to truncate(milestone.title, length: 100), milestone_path %strong= link_to truncate(milestone.title, length: 100), milestone_path
- if milestone.is_group_milestone? - if milestone.group_milestone?
%span - Group Milestone %span - Group Milestone
- else - else
%span - Project Milestone %span - Project Milestone
...@@ -18,10 +18,10 @@ ...@@ -18,10 +18,10 @@
&middot; &middot;
= link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path = link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path
.col-sm-6= milestone_progress_bar(milestone) .col-sm-6= milestone_progress_bar(milestone)
- if milestone.is_a?(GlobalMilestone) || milestone.is_group_milestone? - if milestone.is_a?(GlobalMilestone) || milestone.group_milestone?
.row .row
.col-sm-6 .col-sm-6
- if milestone.is_legacy_group_milestone? - if milestone.legacy_group_milestone?
.expiration= render('shared/milestone_expired', milestone: milestone) .expiration= render('shared/milestone_expired', milestone: milestone)
.projects .projects
- milestone.milestones.each do |milestone| - milestone.milestones.each do |milestone|
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
- if @group - if @group
.col-sm-6.milestone-actions .col-sm-6.milestone-actions
- if can?(current_user, :admin_milestones, @group) - if can?(current_user, :admin_milestones, @group)
- if milestone.is_group_milestone? - if milestone.group_milestone?
= link_to edit_group_milestone_path(@group, milestone), class: "btn btn-xs btn-grouped" do = link_to edit_group_milestone_path(@group, milestone), class: "btn btn-xs btn-grouped" do
Edit Edit
\ \
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
- if group - if group
.pull-right .pull-right
- if can?(current_user, :admin_milestones, group) - if can?(current_user, :admin_milestones, group)
- if milestone.is_group_milestone? - if milestone.group_milestone?
= link_to edit_group_milestone_path(group, milestone), class: "btn btn btn-grouped" do = link_to edit_group_milestone_path(group, milestone), class: "btn btn btn-grouped" do
Edit Edit
- if milestone.active? - if milestone.active?
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
.detail-page-description.milestone-detail .detail-page-description.milestone-detail
%h2.title %h2.title
= markdown_field(milestone, :title) = markdown_field(milestone, :title)
- if @milestone.is_group_milestone? && @milestone.description.present? - if @milestone.group_milestone? && @milestone.description.present?
%div %div
.description .description
.wiki .wiki
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
- close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.' - close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.'
%span All issues for this milestone are closed. #{close_msg} %span All issues for this milestone are closed. #{close_msg}
- if @milestone.is_legacy_group_milestone? || @milestone.is_dashboard_milestone? - if @milestone.legacy_group_milestone? || @milestone.dashboard_milestone?
.table-holder .table-holder
%table.table %table.table
%thead %thead
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
Open Open
%td %td
= ms.expires_at = ms.expires_at
- elsif @milestone.is_group_milestone? - elsif @milestone.group_milestone?
%br %br
View View
= link_to 'Issues', issues_group_path(@group, milestone_title: milestone.title) = link_to 'Issues', issues_group_path(@group, milestone_title: milestone.title)
......
...@@ -4,20 +4,40 @@ class AuthorizedProjectsWorker ...@@ -4,20 +4,40 @@ class AuthorizedProjectsWorker
# Schedules multiple jobs and waits for them to be completed. # Schedules multiple jobs and waits for them to be completed.
def self.bulk_perform_and_wait(args_list) def self.bulk_perform_and_wait(args_list)
# Short-circuit: it's more efficient to do small numbers of jobs inline
return bulk_perform_inline(args_list) if args_list.size <= 3
waiter = Gitlab::JobWaiter.new(args_list.size) waiter = Gitlab::JobWaiter.new(args_list.size)
# Point all the bulk jobs at the same JobWaiter. Converts, [[1], [2], [3]] # Point all the bulk jobs at the same JobWaiter. Converts, [[1], [2], [3]]
# into [[1, "key"], [2, "key"], [3, "key"]] # into [[1, "key"], [2, "key"], [3, "key"]]
waiting_args_list = args_list.map { |args| args << waiter.key } waiting_args_list = args_list.map { |args| [*args, waiter.key] }
bulk_perform_async(waiting_args_list) bulk_perform_async(waiting_args_list)
waiter.wait waiter.wait
end end
# Schedules multiple jobs to run in sidekiq without waiting for completion
def self.bulk_perform_async(args_list) def self.bulk_perform_async(args_list)
Sidekiq::Client.push_bulk('class' => self, 'queue' => sidekiq_options['queue'], 'args' => args_list) Sidekiq::Client.push_bulk('class' => self, 'queue' => sidekiq_options['queue'], 'args' => args_list)
end end
# Performs multiple jobs directly. Failed jobs will be put into sidekiq so
# they can benefit from retries
def self.bulk_perform_inline(args_list)
failed = []
args_list.each do |args|
begin
new.perform(*args)
rescue
failed << args
end
end
bulk_perform_async(failed) if failed.present?
end
def perform(user_id, notify_key = nil) def perform(user_id, notify_key = nil)
user = User.find_by(id: user_id) user = User.find_by(id: user_id)
......
class BuildCoverageWorker class BuildCoverageWorker
include Sidekiq::Worker include Sidekiq::Worker
include BuildQueue include PipelineQueue
def perform(build_id) def perform(build_id)
Ci::Build.find_by(id: build_id)&.update_coverage Ci::Build.find_by(id: build_id)&.update_coverage
......
class BuildFinishedWorker class BuildFinishedWorker
include Sidekiq::Worker include Sidekiq::Worker
include BuildQueue include PipelineQueue
enqueue_in group: :processing
def perform(build_id) def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build| Ci::Build.find_by(id: build_id).try do |build|
......
class BuildHooksWorker class BuildHooksWorker
include Sidekiq::Worker include Sidekiq::Worker
include BuildQueue include PipelineQueue
enqueue_in group: :hooks
def perform(build_id) def perform(build_id)
Ci::Build.find_by(id: build_id) Ci::Build.find_by(id: build_id)
......
class BuildQueueWorker class BuildQueueWorker
include Sidekiq::Worker include Sidekiq::Worker
include BuildQueue include PipelineQueue
enqueue_in group: :processing
def perform(build_id) def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build| Ci::Build.find_by(id: build_id).try do |build|
......
class BuildSuccessWorker class BuildSuccessWorker
include Sidekiq::Worker include Sidekiq::Worker
include BuildQueue include PipelineQueue
enqueue_in group: :processing
def perform(build_id) def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build| Ci::Build.find_by(id: build_id).try do |build|
......
# Concern for setting Sidekiq settings for the various CI build workers.
module BuildQueue
extend ActiveSupport::Concern
included do
sidekiq_options queue: :build
end
end
##
# Concern for setting Sidekiq settings for the various CI pipeline workers. # Concern for setting Sidekiq settings for the various CI pipeline workers.
#
module PipelineQueue module PipelineQueue
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
sidekiq_options queue: :pipeline sidekiq_options queue: 'pipeline_default'
end
class_methods do
def enqueue_in(group:)
raise ArgumentError, 'Unspecified queue group!' if group.empty?
sidekiq_options queue: "pipeline_#{group}"
end
end end
end end
class ExpireJobCacheWorker class ExpireJobCacheWorker
include Sidekiq::Worker include Sidekiq::Worker
include BuildQueue include PipelineQueue
enqueue_in group: :cache
def perform(job_id) def perform(job_id)
job = CommitStatus.joins(:pipeline, :project).find_by(id: job_id) job = CommitStatus.joins(:pipeline, :project).find_by(id: job_id)
......
...@@ -2,6 +2,8 @@ class ExpirePipelineCacheWorker ...@@ -2,6 +2,8 @@ class ExpirePipelineCacheWorker
include Sidekiq::Worker include Sidekiq::Worker
include PipelineQueue include PipelineQueue
enqueue_in group: :cache
def perform(pipeline_id) def perform(pipeline_id)
pipeline = Ci::Pipeline.find_by(id: pipeline_id) pipeline = Ci::Pipeline.find_by(id: pipeline_id)
return unless pipeline return unless pipeline
......
...@@ -7,8 +7,6 @@ class MergeWorker ...@@ -7,8 +7,6 @@ class MergeWorker
current_user = User.find(current_user_id) current_user = User.find(current_user_id)
merge_request = MergeRequest.find(merge_request_id) merge_request = MergeRequest.find(merge_request_id)
merge_request.update_column(:merge_jid, jid)
MergeRequests::MergeService.new(merge_request.target_project, current_user, params) MergeRequests::MergeService.new(merge_request.target_project, current_user, params)
.execute(merge_request) .execute(merge_request)
end end
......
...@@ -2,6 +2,8 @@ class PipelineHooksWorker ...@@ -2,6 +2,8 @@ class PipelineHooksWorker
include Sidekiq::Worker include Sidekiq::Worker
include PipelineQueue include PipelineQueue
enqueue_in group: :hooks
def perform(pipeline_id) def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id) Ci::Pipeline.find_by(id: pipeline_id)
.try(:execute_hooks) .try(:execute_hooks)
......
...@@ -2,6 +2,8 @@ class PipelineProcessWorker ...@@ -2,6 +2,8 @@ class PipelineProcessWorker
include Sidekiq::Worker include Sidekiq::Worker
include PipelineQueue include PipelineQueue
enqueue_in group: :processing
def perform(pipeline_id) def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id) Ci::Pipeline.find_by(id: pipeline_id)
.try(:process!) .try(:process!)
......
...@@ -2,6 +2,8 @@ class PipelineSuccessWorker ...@@ -2,6 +2,8 @@ class PipelineSuccessWorker
include Sidekiq::Worker include Sidekiq::Worker
include PipelineQueue include PipelineQueue
enqueue_in group: :processing
def perform(pipeline_id) def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline| Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline|
MergeRequests::MergeWhenPipelineSucceedsService MergeRequests::MergeWhenPipelineSucceedsService
......
...@@ -2,6 +2,8 @@ class PipelineUpdateWorker ...@@ -2,6 +2,8 @@ class PipelineUpdateWorker
include Sidekiq::Worker include Sidekiq::Worker
include PipelineQueue include PipelineQueue
enqueue_in group: :processing
def perform(pipeline_id) def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id) Ci::Pipeline.find_by(id: pipeline_id)
.try(:update_status) .try(:update_status)
......
...@@ -2,6 +2,8 @@ class StageUpdateWorker ...@@ -2,6 +2,8 @@ class StageUpdateWorker
include Sidekiq::Worker include Sidekiq::Worker
include PipelineQueue include PipelineQueue
enqueue_in group: :processing
def perform(stage_id) def perform(stage_id)
Ci::Stage.find_by(id: stage_id).try do |stage| Ci::Stage.find_by(id: stage_id).try do |stage|
stage.update_status stage.update_status
......
---
title: Prevents rendering empty badges when request fails
merge_request:
author:
type: fixed
---
title: Remove `is_` prefix from predicate method names
merge_request: 13810
author: Maxim Rydkin
type: other
---
title: Instrument MergeRequest#ensure_ref_fetched
merge_request:
author:
type: other
---
title: Present enqueued merge jobs as Merging as well
merge_request:
author:
---
title: Update and fix resolvable note icons for easier recognition
merge_request:
author:
type: changed
---
title: Never wait for sidekiq jobs when creating projects
merge_request: 13775
author:
type: other
---
title: Disable GitLab Project Import Button if source disabled
merge_request:
author:
type: fixed
---
title: Fix incorrect date/time formatting on prometheus graphs
merge_request: 13865
author:
type: fixed
---
title: Fixed: Notifications weren't sending to participating users with a `Custom` notification setting.
merge_request: 13680
author: jneen
type: fixed
---
title: Fixed diff changes bar buttons from showing/hiding whilst scrolling
merge_request:
author:
type: fixed
---
title: Update 'Using Docker images' documentation
merge_request: 13848
author:
type: other
---
title: Fix events error importing GitLab projects
merge_request:
author:
type: fixed
---
title: Fix pipeline trigger via API fails with 500 Internal Server Error in 9.5
merge_request:
author:
type: fixed
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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