Merge branch 'master' into ph-inline-js

This commit is contained in:
Phil Hughes 2017-07-27 16:00:30 +01:00
commit 76704c7960
243 changed files with 4290 additions and 1251 deletions

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
7.5

View File

@ -2,6 +2,18 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 9.4.1 (2017-07-25)
- Fix pipeline_schedules pages throwing error 500 (when ref is empty). !12983
- Fix editing project with container images present. !13028
- Fix some invalid entries in PO files. !13032
- Fix cross site request protection when logging in as a regular user when LDAP is enabled. !13049
- Fix bug causing metrics files to be truncated. !35420
- Fix anonymous access to public projects in groups with pending invites.
- Fixed issue boards sidebar close icon size.
- Fixed duplicate new milestone buttons when new navigation is turned on.
- Fix margins in the mini graph for pipeline in commits box.
## 9.4.0 (2017-07-22) ## 9.4.0 (2017-07-22)
- Add blame view age mapping. !7198 (Jeff Stubler) - Add blame view age mapping. !7198 (Jeff Stubler)

View File

@ -114,8 +114,8 @@ scheduling into milestones. Labelling is a task for everyone.
Most issues will have labels for at least one of the following: Most issues will have labels for at least one of the following:
- Type: ~"feature proposal", ~bug, ~customer, etc. - Type: ~"feature proposal", ~bug, ~customer, etc.
- Subject: ~wiki, ~"container registry", ~ldap, ~api, etc. - Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
- Team: ~CI, ~Discussion, ~Edge, ~Frontend, ~Platform, etc. - Team: ~CI, ~Discussion, ~Edge, ~Platform, etc.
- Priority: ~Deliverable, ~Stretch - Priority: ~Deliverable, ~Stretch
All labels, their meaning and priority are defined on the All labels, their meaning and priority are defined on the
@ -278,7 +278,7 @@ For feature proposals for EE, open an issue on the
In order to help track the feature proposals, we have created a In order to help track the feature proposals, we have created a
[`feature proposal`][fpl] label. For the time being, users that are not members [`feature proposal`][fpl] label. For the time being, users that are not members
of the project cannot add labels. You can instead ask one of the [core team] of the project cannot add labels. You can instead ask one of the [core team]
members to add the label `feature proposal` to the issue or add the following members to add the label ~"feature proposal" to the issue or add the following
code snippet right after your description in a new line: `~"feature proposal"`. code snippet right after your description in a new line: `~"feature proposal"`.
Please keep feature proposals as small and simple as possible, complex ones Please keep feature proposals as small and simple as possible, complex ones

View File

@ -16,6 +16,7 @@ gem 'mysql2', '~> 0.4.5', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.25.1.1' gem 'rugged', '~> 0.25.1.1'
gem 'grape-route-helpers', '~> 2.0.0'
gem 'faraday', '~> 0.12' gem 'faraday', '~> 0.12'
@ -60,7 +61,8 @@ gem 'browser', '~> 2.2'
# LDAP Auth # LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes # GitLab fork with several improvements to original library. For full list of changes
# see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master # see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master
gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap' gem 'gitlab_omniauth-ldap', '~> 2.0.3', require: 'omniauth-ldap'
gem 'net-ldap'
# Git Wiki # Git Wiki
# Required manually in config/initializers/gollum.rb to control load order # Required manually in config/initializers/gollum.rb to control load order
@ -164,7 +166,7 @@ gem 'rainbow', '~> 2.2'
gem 'settingslogic', '~> 2.0.9' gem 'settingslogic', '~> 2.0.9'
# Linear-time regex library for untrusted regular expressions # Linear-time regex library for untrusted regular expressions
gem 're2', '~> 1.1.0' gem 're2', '~> 1.1.1'
# Misc # Misc

View File

@ -288,11 +288,11 @@ GEM
mime-types (>= 1.16, < 3) mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab-markup (1.5.1) gitlab-markup (1.5.1)
gitlab_omniauth-ldap (1.2.1) gitlab_omniauth-ldap (2.0.3)
net-ldap (~> 0.9) net-ldap (~> 0.16)
omniauth (~> 1.0) omniauth (~> 1.3)
pyu-ruby-sasl (~> 0.0.3.1) pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.3) rubyntlm (~> 0.5)
globalid (0.3.7) globalid (0.3.7)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
gollum-grit_adapter (1.0.1) gollum-grit_adapter (1.0.1)
@ -345,6 +345,10 @@ GEM
grape-entity (0.6.0) grape-entity (0.6.0)
activesupport activesupport
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
grape-route-helpers (2.0.0)
activesupport
grape (~> 0.16, >= 0.16.0)
rake
grpc (1.4.0) grpc (1.4.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
googleauth (~> 0.5.1) googleauth (~> 0.5.1)
@ -467,7 +471,7 @@ GEM
mustermann-grape (1.0.0) mustermann-grape (1.0.0)
mustermann (~> 1.0.0) mustermann (~> 1.0.0)
mysql2 (0.4.5) mysql2 (0.4.5)
net-ldap (0.12.1) net-ldap (0.16.0)
netrc (0.11.0) netrc (0.11.0)
nokogiri (1.6.8.1) nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0) mini_portile2 (~> 2.1.0)
@ -656,7 +660,7 @@ GEM
debugger-ruby_core_source (~> 1.3) debugger-ruby_core_source (~> 1.3)
rdoc (4.2.2) rdoc (4.2.2)
json (~> 1.4) json (~> 1.4)
re2 (1.1.0) re2 (1.1.1)
recaptcha (3.0.0) recaptcha (3.0.0)
json json
recursive-open-struct (1.0.0) recursive-open-struct (1.0.0)
@ -740,7 +744,7 @@ GEM
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
ruby_parser (3.9.0) ruby_parser (3.9.0)
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
rubyntlm (0.5.2) rubyntlm (0.6.2)
rubypants (0.2.0) rubypants (0.2.0)
rubyzip (1.2.1) rubyzip (1.2.1)
rufus-scheduler (3.4.0) rufus-scheduler (3.4.0)
@ -974,13 +978,14 @@ DEPENDENCIES
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1) gitlab-markup (~> 1.5.1)
gitlab_omniauth-ldap (~> 1.2.1) gitlab_omniauth-ldap (~> 2.0.3)
gollum-lib (~> 4.2) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.4) gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0) gon (~> 6.1.0)
google-api-client (~> 0.8.6) google-api-client (~> 0.8.6)
grape (~> 0.19.2) grape (~> 0.19.2)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
grape-route-helpers (~> 2.0.0)
haml_lint (~> 0.21.0) haml_lint (~> 0.21.0)
hamlit (~> 2.6.1) hamlit (~> 2.6.1)
hashie-forbidden_attributes hashie-forbidden_attributes
@ -1008,6 +1013,7 @@ DEPENDENCIES
minitest (~> 5.7.0) minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6) mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.4.5) mysql2 (~> 0.4.5)
net-ldap
nokogiri (~> 1.6.7, >= 1.6.7.2) nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.4) oauth2 (~> 1.4)
octokit (~> 4.6.2) octokit (~> 4.6.2)
@ -1055,7 +1061,7 @@ DEPENDENCIES
raindrops (~> 0.18) raindrops (~> 0.18)
rblineprof (~> 0.3.6) rblineprof (~> 0.3.6)
rdoc (~> 4.2) rdoc (~> 4.2)
re2 (~> 1.1.0) re2 (~> 1.1.1)
recaptcha (~> 3.0) recaptcha (~> 3.0)
redcarpet (~> 3.4) redcarpet (~> 3.4)
redis (~> 3.2) redis (~> 3.2)

View File

@ -20,6 +20,8 @@
/* global NamespaceSelects */ /* global NamespaceSelects */
/* global Project */ /* global Project */
/* global ProjectAvatar */ /* global ProjectAvatar */
/* global MergeRequest */
/* global Compare */
/* global CompareAutocomplete */ /* global CompareAutocomplete */
/* global ProjectNew */ /* global ProjectNew */
/* global ProjectShow */ /* global ProjectShow */
@ -228,6 +230,19 @@ import initIssuableSidebar from './init_issuable_sidebar';
new gl.IssuableTemplateSelectors(); new gl.IssuableTemplateSelectors();
break; break;
case 'projects:merge_requests:creations:new': case 'projects:merge_requests:creations:new':
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
if (mrNewCompareNode) {
new Compare({
targetProjectUrl: mrNewCompareNode.dataset.targetProjectUrl,
sourceBranchUrl: mrNewCompareNode.dataset.sourceBranchUrl,
targetBranchUrl: mrNewCompareNode.dataset.targetBranchUrl,
});
} else {
const mrNewSubmitNode = document.querySelector('.js-merge-request-new-submit');
new MergeRequest({
action: mrNewSubmitNode.dataset.mrSubmitAction,
});
}
case 'projects:merge_requests:creations:diffs': case 'projects:merge_requests:creations:diffs':
case 'projects:merge_requests:edit': case 'projects:merge_requests:edit':
new gl.Diff(); new gl.Diff();
@ -267,8 +282,14 @@ import initIssuableSidebar from './init_issuable_sidebar';
new gl.Diff(); new gl.Diff();
shortcut_handler = new ShortcutsIssuable(true); shortcut_handler = new ShortcutsIssuable(true);
new ZenMode(); new ZenMode();
initIssuableSidebar(); initIssuableSidebar();
initNotes(); initNotes();
const mrShowNode = document.querySelector('.merge-request');
window.mergeRequest = new MergeRequest({
action: mrShowNode.dataset.mrAction,
});
break; break;
case 'dashboard:activity': case 'dashboard:activity':
new gl.Activities(); new gl.Activities();

View File

@ -3,10 +3,10 @@ document.addEventListener('DOMContentLoaded', () => {
modal: true, modal: true,
show: false, show: false,
}); });
$('.how_to_merge_link').bind('click', () => { $('.how_to_merge_link').on('click', () => {
modal.show(); modal.show();
}); });
$('.modal-header .close').bind('click', () => { $('.modal-header .close').on('click', () => {
modal.hide(); modal.hide();
}); });
}); });

View File

@ -175,7 +175,7 @@ import Cookies from 'js-cookie';
getConflictsCountText() { getConflictsCountText() {
const count = this.getConflictsCount(); const count = this.getConflictsCount();
const text = count ? 'conflicts' : 'conflict'; const text = count > 1 ? 'conflicts' : 'conflict';
return `${count} ${text}`; return `${count} ${text}`;
}, },

View File

@ -108,7 +108,8 @@ export default {
</div> </div>
<mr-widget-memory-usage <mr-widget-memory-usage
v-if="deployment.metrics_url" v-if="deployment.metrics_url"
:metricsUrl="deployment.metrics_url" :metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url"
/> />
</div> </div>
</div> </div>

View File

@ -7,7 +7,14 @@ import MRWidgetService from '../services/mr_widget_service';
export default { export default {
name: 'MemoryUsage', name: 'MemoryUsage',
props: { props: {
metricsUrl: { type: String, required: true }, metricsUrl: {
type: String,
required: true,
},
metricsMonitoringUrl: {
type: String,
required: true,
},
}, },
data() { data() {
return { return {
@ -124,7 +131,7 @@ export default {
<p <p
v-if="shouldShowMemoryGraph" v-if="shouldShowMemoryGraph"
class="usage-info js-usage-info"> class="usage-info js-usage-info">
Memory usage <b>{{memoryChangeType}}</b> from {{memoryFrom}}MB to {{memoryTo}}MB <a :href="metricsMonitoringUrl">Memory</a> usage <b>{{memoryChangeType}}</b> from {{memoryFrom}}MB to {{memoryTo}}MB
</p> </p>
<p <p
v-if="shouldShowLoadFailure" v-if="shouldShowLoadFailure"

View File

@ -21,6 +21,11 @@ header.navbar-gitlab-new {
padding-right: 0; padding-right: 0;
color: currentColor; color: currentColor;
img {
height: 28px;
margin-right: 10px;
}
> a { > a {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -211,6 +211,10 @@
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
&.affix-top .issuable-sidebar {
height: 100%;
}
&.right-sidebar-expanded { &.right-sidebar-expanded {
width: $gutter_width; width: $gutter_width;

View File

@ -597,7 +597,7 @@
} }
// Dropdown button in mini pipeline graph // Dropdown button in mini pipeline graph
.mini-pipeline-graph-dropdown-toggle { button.mini-pipeline-graph-dropdown-toggle {
border-radius: 100px; border-radius: 100px;
background-color: $white-light; background-color: $white-light;
border-width: 1px; border-width: 1px;
@ -608,6 +608,7 @@
padding: 0; padding: 0;
transition: all 0.2s linear; transition: all 0.2s linear;
position: relative; position: relative;
vertical-align: middle;
> .fa.fa-caret-down { > .fa.fa-caret-down {
position: absolute; position: absolute;

View File

@ -76,11 +76,11 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file] params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file]
params.require(:application_setting).permit( params.require(:application_setting).permit(
application_setting_params_ce application_setting_params_attributes
) )
end end
def application_setting_params_ce def application_setting_params_attributes
[ [
:admin_notification_email, :admin_notification_email,
:after_sign_out_path, :after_sign_out_path,

View File

@ -1,6 +1,6 @@
class Admin::DashboardController < Admin::ApplicationController class Admin::DashboardController < Admin::ApplicationController
def index def index
@projects = Project.with_route.limit(10) @projects = Project.without_deleted.with_route.limit(10)
@users = User.limit(10) @users = User.limit(10)
@groups = Group.with_route.limit(10) @groups = Group.with_route.limit(10)
end end

View File

@ -22,6 +22,7 @@ class Projects::ApplicationController < ApplicationController
def project def project
return @project if @project return @project if @project
return nil unless params[:project_id] || params[:id]
path = File.join(params[:namespace_id], params[:project_id] || params[:id]) path = File.join(params[:namespace_id], params[:project_id] || params[:id])
auth_proc = ->(project) { !project.pending_delete? } auth_proc = ->(project) { !project.pending_delete? }

View File

@ -223,12 +223,18 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
if can?(current_user, :read_environment, environment) && environment.has_metrics? if can?(current_user, :read_environment, environment) && environment.has_metrics?
metrics_project_environment_deployment_path(environment.project, environment, deployment) metrics_project_environment_deployment_path(environment.project, environment, deployment)
end end
metrics_monitoring_url =
if can?(current_user, :read_environment, environment)
environment_metrics_path(environment)
end
{ {
id: environment.id, id: environment.id,
name: environment.name, name: environment.name,
url: project_environment_path(project, environment), url: project_environment_path(project, environment),
metrics_url: metrics_url, metrics_url: metrics_url,
metrics_monitoring_url: metrics_monitoring_url,
stop_url: stop_url, stop_url: stop_url,
external_url: environment.external_url, external_url: environment.external_url,
external_url_formatted: environment.formatted_external_url, external_url_formatted: environment.formatted_external_url,

View File

@ -296,10 +296,10 @@ class ProjectsController < Projects::ApplicationController
def project_params def project_params
params.require(:project) params.require(:project)
.permit(project_params_ce) .permit(project_params_attributes)
end end
def project_params_ce def project_params_attributes
[ [
:avatar, :avatar,
:build_allow_git_fetch, :build_allow_git_fetch,

View File

@ -11,6 +11,7 @@
# group_id: integer # group_id: integer
# project_id: integer # project_id: integer
# milestone_title: string # milestone_title: string
# author_id: integer
# assignee_id: integer # assignee_id: integer
# search: string # search: string
# label_name: string # label_name: string

View File

@ -18,7 +18,8 @@ module SystemNoteHelper
'milestone' => 'icon_clock_o', 'milestone' => 'icon_clock_o',
'discussion' => 'icon_comment_o', 'discussion' => 'icon_comment_o',
'moved' => 'icon_arrow_circle_o_right', 'moved' => 'icon_arrow_circle_o_right',
'outdated' => 'icon_edit' 'outdated' => 'icon_edit',
'duplicate' => 'icon_clone'
}.freeze }.freeze
def icon_for_system_note(note) def icon_for_system_note(note)

View File

@ -21,7 +21,7 @@ module Ci
has_many :merge_requests, foreign_key: "head_pipeline_id" has_many :merge_requests, foreign_key: "head_pipeline_id"
has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :retryable_builds, -> { latest.failed_or_canceled }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus' has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus'
has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :artifacts, -> { latest.with_artifacts_not_expired.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :artifacts, -> { latest.with_artifacts_not_expired.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build'

View File

@ -40,10 +40,6 @@ module Ci
update_attribute(:active, false) update_attribute(:active, false)
end end
def runnable_by_owner?
Ability.allowed?(owner, :create_pipeline, project)
end
def set_next_run_at def set_next_run_at
self.next_run_at = Gitlab::Ci::CronParser.new(cron, cron_timezone).next_time_from(Time.now) self.next_run_at = Gitlab::Ci::CronParser.new(cron, cron_timezone).next_time_from(Time.now)
end end

View File

@ -17,7 +17,13 @@ module ProtectedRef
class_methods do class_methods do
def protected_ref_access_levels(*types) def protected_ref_access_levels(*types)
types.each do |type| types.each do |type|
has_many :"#{type}_access_levels", dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # We need to set `inverse_of` to make sure the `belongs_to`-object is set
# when creating children using `accepts_nested_attributes_for`.
#
# 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
# to fail.
has_many :"#{type}_access_levels", dependent: :destroy, 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}." }
@ -25,8 +31,8 @@ module ProtectedRef
end end
end end
def protected_ref_accessible_to?(ref, user, action:) def protected_ref_accessible_to?(ref, user, action:, protected_refs: nil)
access_levels_for_ref(ref, action: action).any? do |access_level| access_levels_for_ref(ref, action: action, protected_refs: protected_refs).any? do |access_level|
access_level.check_access(user) access_level.check_access(user)
end end
end end
@ -37,8 +43,9 @@ module ProtectedRef
end end
end end
def access_levels_for_ref(ref, action:) def access_levels_for_ref(ref, action:, protected_refs: nil)
self.matching(ref).map(&:"#{action}_access_levels").flatten self.matching(ref, protected_refs: protected_refs)
.map(&:"#{action}_access_levels").flatten
end end
def matching(ref_name, protected_refs: nil) def matching(ref_name, protected_refs: nil)

View File

@ -236,10 +236,21 @@ class MergeRequestDiff < ActiveRecord::Base
def create_merge_request_diff_files(diffs) def create_merge_request_diff_files(diffs)
rows = diffs.map.with_index do |diff, index| rows = diffs.map.with_index do |diff, index|
diff.to_hash.merge( diff_hash = diff.to_hash.merge(
binary: false,
merge_request_diff_id: self.id, merge_request_diff_id: self.id,
relative_order: index relative_order: index
) )
# Compatibility with old diffs created with Psych.
diff_hash.tap do |hash|
diff_text = hash[:diff]
if diff_text.encoding == Encoding::BINARY && !diff_text.ascii_only?
hash[:binary] = true
hash[:diff] = [diff_text].pack('m0')
end
end
end end
Gitlab::Database.bulk_insert('merge_request_diff_files', rows) Gitlab::Database.bulk_insert('merge_request_diff_files', rows)
@ -268,9 +279,7 @@ class MergeRequestDiff < ActiveRecord::Base
st_diffs st_diffs
end end
elsif merge_request_diff_files.present? elsif merge_request_diff_files.present?
merge_request_diff_files merge_request_diff_files.map(&:to_hash)
.as_json(only: Gitlab::Git::Diff::SERIALIZE_KEYS)
.map(&:with_indifferent_access)
end end
end end

View File

@ -8,4 +8,14 @@ class MergeRequestDiffFile < ActiveRecord::Base
encode_utf8(diff) if diff.respond_to?(:encoding) encode_utf8(diff) if diff.respond_to?(:encoding)
end end
def diff
binary? ? super.unpack('m0').first : super
end
def to_hash
keys = Gitlab::Git::Diff::SERIALIZE_KEYS - [:diff]
as_json(only: keys).merge(diff: diff).with_indifferent_access
end
end end

View File

@ -3,10 +3,8 @@ class JiraService < IssueTrackerService
validates :url, url: true, presence: true, if: :activated? validates :url, url: true, presence: true, if: :activated?
validates :api_url, url: true, allow_blank: true validates :api_url, url: true, allow_blank: true
validates :project_key, presence: true, if: :activated?
prop_accessor :username, :password, :url, :api_url, :project_key, prop_accessor :username, :password, :url, :api_url, :jira_issue_transition_id, :title, :description
:jira_issue_transition_id, :title, :description
before_update :reset_password before_update :reset_password
@ -54,10 +52,6 @@ class JiraService < IssueTrackerService
@client ||= JIRA::Client.new(options) @client ||= JIRA::Client.new(options)
end end
def jira_project
@jira_project ||= jira_request { client.Project.find(project_key) }
end
def help def help
"You need to configure JIRA before enabling this service. For more details "You need to configure JIRA before enabling this service. For more details
read the read the
@ -88,18 +82,12 @@ class JiraService < IssueTrackerService
[ [
{ type: 'text', name: 'url', title: 'Web URL', placeholder: 'https://jira.example.com', required: true }, { type: 'text', name: 'url', title: 'Web URL', placeholder: 'https://jira.example.com', required: true },
{ type: 'text', name: 'api_url', title: 'JIRA API URL', placeholder: 'If different from Web URL' }, { type: 'text', name: 'api_url', title: 'JIRA API URL', placeholder: 'If different from Web URL' },
{ type: 'text', name: 'project_key', placeholder: 'Project Key', required: true },
{ type: 'text', name: 'username', placeholder: '', required: true }, { type: 'text', name: 'username', placeholder: '', required: true },
{ type: 'password', name: 'password', placeholder: '', required: true }, { type: 'password', name: 'password', placeholder: '', required: true },
{ type: 'text', name: 'jira_issue_transition_id', placeholder: '' } { type: 'text', name: 'jira_issue_transition_id', title: 'Transition ID', placeholder: '' }
] ]
end end
# URLs to redirect from Gitlab issues pages to jira issue tracker
def project_url
"#{url}/issues/?jql=project=#{project_key}"
end
def issues_url def issues_url
"#{url}/browse/:id" "#{url}/browse/:id"
end end
@ -184,7 +172,7 @@ class JiraService < IssueTrackerService
def test_settings def test_settings
return unless client_url.present? return unless client_url.present?
# Test settings by getting the project # Test settings by getting the project
jira_request { jira_project.present? } jira_request { client.ServerInfo.all.attrs }
end end
private private

View File

@ -471,8 +471,17 @@ class Repository
end end
cache_method :root_ref cache_method :root_ref
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/314
def exists? def exists?
refs_directory_exists? return false unless path_with_namespace
Gitlab::GitalyClient.migrate(:repository_exists) do |enabled|
if enabled
raw_repository.exists?
else
refs_directory_exists?
end
end
end end
cache_method :exists? cache_method :exists?
@ -1095,8 +1104,6 @@ class Repository
end end
def refs_directory_exists? def refs_directory_exists?
return false unless path_with_namespace
File.exist?(File.join(path_to_repo, 'refs')) File.exist?(File.join(path_to_repo, 'refs'))
end end

View File

@ -1,7 +1,8 @@
class SystemNoteMetadata < ActiveRecord::Base class SystemNoteMetadata < ActiveRecord::Base
ICON_TYPES = %w[ ICON_TYPES = %w[
commit description merge confidential visible label assignee cross_reference commit description merge confidential visible label assignee cross_reference
title time_tracking branch milestone discussion task moved opened closed merged title time_tracking branch milestone discussion task moved
opened closed merged duplicate
outdated outdated
].freeze ].freeze

View File

@ -1,17 +1,15 @@
module Ci module Ci
class BuildPolicy < CommitStatusPolicy class BuildPolicy < CommitStatusPolicy
condition(:protected_action) do condition(:protected_ref) do
next false unless @subject.action?
access = ::Gitlab::UserAccess.new(@user, project: @subject.project) access = ::Gitlab::UserAccess.new(@user, project: @subject.project)
if @subject.tag? if @subject.tag?
!access.can_create_tag?(@subject.ref) !access.can_create_tag?(@subject.ref)
else else
!access.can_merge_to_branch?(@subject.ref) !access.can_update_branch?(@subject.ref)
end end
end end
rule { protected_action }.prevent :update_build rule { protected_ref }.prevent :update_build
end end
end end

View File

@ -1,5 +1,17 @@
module Ci module Ci
class PipelinePolicy < BasePolicy class PipelinePolicy < BasePolicy
delegate { @subject.project } delegate { @subject.project }
condition(:protected_ref) do
access = ::Gitlab::UserAccess.new(@user, project: @subject.project)
if @subject.tag?
!access.can_create_tag?(@subject.ref)
else
!access.can_update_branch?(@subject.ref)
end
end
rule { protected_ref }.prevent :update_pipeline
end end
end end

View File

@ -44,7 +44,7 @@ class GlobalPolicy < BasePolicy
prevent :log_in prevent :log_in
end end
rule { ~restricted_public_level }.policy do rule { admin | ~restricted_public_level }.policy do
enable :read_users_list enable :read_users_list
end end
end end

View File

@ -16,7 +16,8 @@ class BuildDetailsEntity < JobEntity
end end
expose :path do |build| expose :path do |build|
project_merge_request_path(project, build.merge_request) project_merge_request_path(build.merge_request.project,
build.merge_request)
end end
end end

View File

@ -9,7 +9,7 @@ class DeployKeyEntity < Grape::Entity
expose :created_at expose :created_at
expose :updated_at expose :updated_at
expose :projects, using: ProjectEntity do |deploy_key| expose :projects, using: ProjectEntity do |deploy_key|
deploy_key.projects.select { |project| options[:user].can?(:read_project, project) } deploy_key.projects.without_deleted.select { |project| options[:user].can?(:read_project, project) }
end end
expose :can_edit expose :can_edit

View File

@ -15,12 +15,40 @@ module Ci
pipeline_schedule: schedule pipeline_schedule: schedule
) )
result = validate(current_user || trigger_request.trigger.owner,
ignore_skip_ci: ignore_skip_ci,
save_on_errors: save_on_errors)
return result if result
Ci::Pipeline.transaction do
update_merge_requests_head_pipeline if pipeline.save
Ci::CreatePipelineStagesService
.new(project, current_user)
.execute(pipeline)
end
cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
pipeline_created_counter.increment(source: source)
pipeline.tap(&:process!)
end
private
def validate(triggering_user, ignore_skip_ci:, save_on_errors:)
unless project.builds_enabled? unless project.builds_enabled?
return error('Pipeline is disabled') return error('Pipeline is disabled')
end end
unless trigger_request || can?(current_user, :create_pipeline, project) unless allowed_to_trigger_pipeline?(triggering_user)
return error('Insufficient permissions to create a new pipeline') if can?(triggering_user, :create_pipeline, project)
return error("Insufficient permissions for protected ref '#{ref}'")
else
return error('Insufficient permissions to create a new pipeline')
end
end end
unless branch? || tag? unless branch? || tag?
@ -46,23 +74,28 @@ module Ci
unless pipeline.has_stage_seeds? unless pipeline.has_stage_seeds?
return error('No stages / jobs for this pipeline.') return error('No stages / jobs for this pipeline.')
end end
Ci::Pipeline.transaction do
update_merge_requests_head_pipeline if pipeline.save
Ci::CreatePipelineStagesService
.new(project, current_user)
.execute(pipeline)
end
cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
pipeline_created_counter.increment(source: source)
pipeline.tap(&:process!)
end end
private def allowed_to_trigger_pipeline?(triggering_user)
if triggering_user
allowed_to_create?(triggering_user)
else # legacy triggers don't have a corresponding user
!project.protected_for?(ref)
end
end
def allowed_to_create?(triggering_user)
access = Gitlab::UserAccess.new(triggering_user, project: project)
can?(triggering_user, :create_pipeline, project) &&
if branch?
access.can_update_branch?(ref)
elsif tag?
access.can_create_tag?(ref)
else
true # Allow it for now and we'll reject when we check ref existence
end
end
def update_merge_requests_head_pipeline def update_merge_requests_head_pipeline
return unless pipeline.latest? return unless pipeline.latest?
@ -113,15 +146,21 @@ module Ci
end end
def branch? def branch?
project.repository.ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + ref) return @is_branch if defined?(@is_branch)
@is_branch =
project.repository.ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + ref)
end end
def tag? def tag?
project.repository.ref_exists?(Gitlab::Git::TAG_REF_PREFIX + ref) return @is_tag if defined?(@is_tag)
@is_tag =
project.repository.ref_exists?(Gitlab::Git::TAG_REF_PREFIX + ref)
end end
def ref def ref
Gitlab::Git.ref_name(origin_ref) @ref ||= Gitlab::Git.ref_name(origin_ref)
end end
def valid_sha? def valid_sha?

View File

@ -1,12 +1,14 @@
module Ci module Ci
class CreateTriggerRequestService module CreateTriggerRequestService
def execute(project, trigger, ref, variables = nil) Result = Struct.new(:trigger_request, :pipeline)
def self.execute(project, trigger, ref, variables = nil)
trigger_request = trigger.trigger_requests.create(variables: variables) trigger_request = trigger.trigger_requests.create(variables: variables)
pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: ref) pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: ref)
.execute(:trigger, ignore_skip_ci: true, trigger_request: trigger_request) .execute(:trigger, ignore_skip_ci: true, trigger_request: trigger_request)
trigger_request if pipeline.persisted? Result.new(trigger_request, pipeline)
end end
end end
end end

View File

@ -58,6 +58,7 @@ class IssuableBaseService < BaseService
params.delete(:assignee_ids) params.delete(:assignee_ids)
params.delete(:assignee_id) params.delete(:assignee_id)
params.delete(:due_date) params.delete(:due_date)
params.delete(:canonical_issue_id)
end end
filter_assignee(issuable) filter_assignee(issuable)

View File

@ -7,6 +7,14 @@ module Issues
issue_data issue_data
end end
def reopen_service
Issues::ReopenService
end
def close_service
Issues::CloseService
end
private private
def create_assignee_note(issue, old_assignees) def create_assignee_note(issue, old_assignees)

View File

@ -0,0 +1,24 @@
module Issues
class DuplicateService < Issues::BaseService
def execute(duplicate_issue, canonical_issue)
return if canonical_issue == duplicate_issue
return unless can?(current_user, :update_issue, duplicate_issue)
return unless can?(current_user, :create_note, canonical_issue)
create_issue_duplicate_note(duplicate_issue, canonical_issue)
create_issue_canonical_note(canonical_issue, duplicate_issue)
close_service.new(project, current_user, {}).execute(duplicate_issue)
end
private
def create_issue_duplicate_note(duplicate_issue, canonical_issue)
SystemNoteService.mark_duplicate_issue(duplicate_issue, duplicate_issue.project, current_user, canonical_issue)
end
def create_issue_canonical_note(canonical_issue, duplicate_issue)
SystemNoteService.mark_canonical_issue_of_duplicate(canonical_issue, canonical_issue.project, current_user, duplicate_issue)
end
end
end

View File

@ -5,6 +5,7 @@ module Issues
def execute(issue) def execute(issue)
handle_move_between_iids(issue) handle_move_between_iids(issue)
filter_spam_check_params filter_spam_check_params
change_issue_duplicate(issue)
update(issue) update(issue)
end end
@ -53,14 +54,6 @@ module Issues
end end
end end
def reopen_service
Issues::ReopenService
end
def close_service
Issues::CloseService
end
def handle_move_between_iids(issue) def handle_move_between_iids(issue)
return unless params[:move_between_iids] return unless params[:move_between_iids]
@ -72,6 +65,15 @@ module Issues
issue.move_between(issue_before, issue_after) issue.move_between(issue_before, issue_after)
end end
def change_issue_duplicate(issue)
canonical_issue_id = params.delete(:canonical_issue_id)
canonical_issue = IssuesFinder.new(current_user).find_by(id: canonical_issue_id)
if canonical_issue
Issues::DuplicateService.new(project, current_user).execute(issue, canonical_issue)
end
end
private private
def get_issue_if_allowed(project, iid) def get_issue_if_allowed(project, iid)

View File

@ -15,40 +15,48 @@ module Projects
def execute def execute
return false unless can?(current_user, :remove_project, project) return false unless can?(current_user, :remove_project, project)
repo_path = project.path_with_namespace
wiki_path = repo_path + '.wiki'
# Flush the cache for both repositories. This has to be done _before_ # Flush the cache for both repositories. This has to be done _before_
# removing the physical repositories as some expiration code depends on # removing the physical repositories as some expiration code depends on
# Git data (e.g. a list of branch names). # Git data (e.g. a list of branch names).
flush_caches(project, wiki_path) flush_caches(project)
Projects::UnlinkForkService.new(project, current_user).execute Projects::UnlinkForkService.new(project, current_user).execute
Project.transaction do attempt_destroy_transaction(project)
project.team.truncate
project.destroy!
unless remove_legacy_registry_tags
raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.')
end
unless remove_repository(repo_path)
raise_error('Failed to remove project repository. Please try again or contact administrator.')
end
unless remove_repository(wiki_path)
raise_error('Failed to remove wiki repository. Please try again or contact administrator.')
end
end
log_info("Project \"#{project.path_with_namespace}\" was removed")
system_hook_service.execute_hooks_for(project, :destroy) system_hook_service.execute_hooks_for(project, :destroy)
log_info("Project \"#{project.full_path}\" was removed")
true true
rescue => error
attempt_rollback(project, error.message)
false
rescue Exception => error # rubocop:disable Lint/RescueException
# Project.transaction can raise Exception
attempt_rollback(project, error.message)
raise
end end
private private
def repo_path
project.path_with_namespace
end
def wiki_path
repo_path + '.wiki'
end
def trash_repositories!
unless remove_repository(repo_path)
raise_error('Failed to remove project repository. Please try again or contact administrator.')
end
unless remove_repository(wiki_path)
raise_error('Failed to remove wiki repository. Please try again or contact administrator.')
end
end
def remove_repository(path) def remove_repository(path)
# Skip repository removal. We use this flag when remove user or group # Skip repository removal. We use this flag when remove user or group
return true if params[:skip_repo] == true return true if params[:skip_repo] == true
@ -70,6 +78,26 @@ module Projects
end end
end end
def attempt_rollback(project, message)
return unless project
project.update_attributes(delete_error: message, pending_delete: false)
log_error("Deletion failed on #{project.full_path} with the following message: #{message}")
end
def attempt_destroy_transaction(project)
Project.transaction do
unless remove_legacy_registry_tags
raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.')
end
trash_repositories!
project.team.truncate
project.destroy!
end
end
## ##
# This method makes sure that we correctly remove registry tags # This method makes sure that we correctly remove registry tags
# for legacy image repository (when repository path equals project path). # for legacy image repository (when repository path equals project path).
@ -96,7 +124,7 @@ module Projects
"#{path}+#{project.id}#{DELETED_FLAG}" "#{path}+#{project.id}#{DELETED_FLAG}"
end end
def flush_caches(project, wiki_path) def flush_caches(project)
project.repository.before_delete project.repository.before_delete
Repository.new(wiki_path, project).before_delete Repository.new(wiki_path, project).before_delete

View File

@ -130,7 +130,11 @@ module Projects
end end
def max_size def max_size
current_application_settings.max_pages_size.megabytes || MAX_SIZE max_pages_size = current_application_settings.max_pages_size.megabytes
return MAX_SIZE if max_pages_size.zero?
[max_pages_size, MAX_SIZE].min
end end
def tmp_path def tmp_path

View File

@ -471,6 +471,24 @@ module QuickActions
end end
end end
desc 'Mark this issue as a duplicate of another issue'
explanation do |duplicate_reference|
"Marks this issue as a duplicate of #{duplicate_reference}."
end
params '#issue'
condition do
issuable.is_a?(Issue) &&
issuable.persisted? &&
current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
end
command :duplicate do |duplicate_param|
canonical_issue = extract_references(duplicate_param, :issue).first
if canonical_issue.present?
@updates[:canonical_issue_id] = canonical_issue.id
end
end
def extract_users(params) def extract_users(params)
return [] if params.nil? return [] if params.nil?

View File

@ -552,6 +552,44 @@ module SystemNoteService
create_note(NoteSummary.new(noteable, project, author, body, action: 'moved')) create_note(NoteSummary.new(noteable, project, author, body, action: 'moved'))
end end
# Called when a Noteable has been marked as a duplicate of another Issue
#
# noteable - Noteable object
# project - Project owning noteable
# author - User performing the change
# canonical_issue - Issue that this is a duplicate of
#
# Example Note text:
#
# "marked this issue as a duplicate of #1234"
#
# "marked this issue as a duplicate of other_project#5678"
#
# Returns the created Note object
def mark_duplicate_issue(noteable, project, author, canonical_issue)
body = "marked this issue as a duplicate of #{canonical_issue.to_reference(project)}"
create_note(NoteSummary.new(noteable, project, author, body, action: 'duplicate'))
end
# Called when a Noteable has been marked as the canonical Issue of a duplicate
#
# noteable - Noteable object
# project - Project owning noteable
# author - User performing the change
# duplicate_issue - Issue that was a duplicate of this
#
# Example Note text:
#
# "marked #1234 as a duplicate of this issue"
#
# "marked other_project#5678 as a duplicate of this issue"
#
# Returns the created Note object
def mark_canonical_issue_of_duplicate(noteable, project, author, duplicate_issue)
body = "marked #{duplicate_issue.to_reference(project)} as a duplicate of this issue"
create_note(NoteSummary.new(noteable, project, author, body, action: 'duplicate'))
end
private private
def notes_for_mentioner(mentioner, noteable, notes) def notes_for_mentioner(mentioner, noteable, notes)

View File

@ -150,7 +150,7 @@
.well-segment.well-centered .well-segment.well-centered
= link_to admin_groups_path do = link_to admin_groups_path do
%h3.text-center %h3.text-center
Groups Groups:
= number_with_delimiter(Group.count) = number_with_delimiter(Group.count)
%hr %hr
= link_to 'New group', new_admin_group_path, class: "btn btn-new" = link_to 'New group', new_admin_group_path, class: "btn btn-new"

View File

@ -0,0 +1,6 @@
- project = local_assigns.fetch(:project)
- return unless project.delete_error.present?
.project-deletion-failed-message.alert.alert-warning
This project was scheduled for deletion, but failed with the following message:
= project.delete_error

View File

@ -0,0 +1,8 @@
- project = local_assigns.fetch(:project)
- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message
= content_for flash_message_container do
= render partial: 'deletion_failed', locals: { project: project }
- if current_user && can?(current_user, :download_code, project)
= render 'shared/no_ssh'
= render 'shared/no_password'

View File

@ -1,10 +1,6 @@
- @no_container = true - @no_container = true
- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message
= content_for flash_message_container do = render partial: 'flash_messages', locals: { project: @project }
- if current_user && can?(current_user, :download_code, @project)
= render 'shared/no_ssh'
= render 'shared/no_password'
= render "projects/head" = render "projects/head"
= render "home_panel" = render "home_panel"

View File

@ -1,6 +1,7 @@
- @no_container = true - @no_container = true
- page_title "Labels" - page_title "Labels"
- hide_class = '' - hide_class = ''
- can_admin_label = can?(current_user, :admin_label, @project)
- if show_new_nav? && can?(current_user, :admin_label, @project) - if show_new_nav? && can?(current_user, :admin_label, @project)
- content_for :breadcrumbs_extra do - content_for :breadcrumbs_extra do
@ -12,15 +13,17 @@
%div{ class: container_class } %div{ class: container_class }
.top-area.adjust .top-area.adjust
.nav-text .nav-text
Labels can be applied to issues and merge requests. Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging. Labels can be applied to issues and merge requests.
- if can_admin_label
Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.
.nav-controls{ class: ("visible-xs" if show_new_nav?) } - if can_admin_label
- if can?(current_user, :admin_label, @project) .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= link_to new_project_label_path(@project), class: "btn btn-new" do = link_to new_project_label_path(@project), class: "btn btn-new" do
New label New label
.labels .labels
- if can?(current_user, :admin_label, @project) - if can_admin_label
-# Only show it in the first page -# Only show it in the first page
- hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1')
.prioritized-labels{ class: ('hide' if hide) } .prioritized-labels{ class: ('hide' if hide) }
@ -33,7 +36,7 @@
- if @labels.present? - if @labels.present?
.other-labels .other-labels
- if can?(current_user, :admin_label, @project) - if can_admin_label
%h5{ class: ('hide' if hide) } Other Labels %h5{ class: ('hide' if hide) } Other Labels
%ul.content-list.manage-labels-list.js-other-labels %ul.content-list.manage-labels-list.js-other-labels
= render partial: 'shared/label', subject: @project, collection: @labels, as: :label = render partial: 'shared/label', subject: @project, collection: @labels, as: :label

View File

@ -3,7 +3,7 @@
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form form-inline js-requires-input" } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form form-inline js-requires-input" } do |f|
.hide.alert.alert-danger.mr-compare-errors .hide.alert.alert-danger.mr-compare-errors
.merge-request-branches.row .merge-request-branches.js-merge-request-new-compare.row{ 'data-target-project-url': project_new_merge_request_update_branches_path(@source_project), 'data-source-branch-url': project_new_merge_request_branch_from_path(@source_project), 'data-target-branch-url': project_new_merge_request_branch_to_path(@source_project) }
.col-md-6 .col-md-6
.panel.panel-default.panel-new-merge-request .panel.panel-default.panel-new-merge-request
.panel-heading .panel-heading
@ -66,10 +66,3 @@
- if @merge_request.errors.any? - if @merge_request.errors.any?
= form_errors(@merge_request) = form_errors(@merge_request)
= f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn" = f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn"
:javascript
new Compare({
targetProjectUrl: "#{project_new_merge_request_update_branches_path(@source_project)}",
sourceBranchUrl: "#{project_new_merge_request_branch_from_path(@source_project)}",
targetBranchUrl: "#{project_new_merge_request_branch_to_path(@source_project)}"
});

View File

@ -17,7 +17,7 @@
= f.hidden_field :target_project_id = f.hidden_field :target_project_id
= f.hidden_field :target_branch = f.hidden_field :target_branch
.mr-compare.merge-request .mr-compare.merge-request.js-merge-request-new-submit{ 'data-mr-submit-action': "#{j params[:tab].presence || 'new'}" }
- if @commits.empty? - if @commits.empty?
.commits-empty .commits-empty
%h4 %h4
@ -50,8 +50,3 @@
.mr-loading-status .mr-loading-status
= spinner = spinner
:javascript
var merge_request = new MergeRequest({
action: "#{j params[:tab].presence || 'new'}",
});

View File

@ -3,10 +3,10 @@
- page_description @merge_request.description - page_description @merge_request.description
- page_card_attributes @merge_request.card_attributes - page_card_attributes @merge_request.card_attributes
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('diff_notes') = webpack_bundle_tag('diff_notes')
.merge-request{ 'data-url' => merge_request_path(@merge_request, format: :json), 'data-project-path' => project_path(@merge_request.project) } .merge-request{ 'data-mr-action': "#{j params[:tab].presence || 'show'}", 'data-url' => merge_request_path(@merge_request, format: :json), 'data-project-path' => project_path(@merge_request.project) }
= render "projects/merge_requests/mr_title" = render "projects/merge_requests/mr_title"
.merge-request-details.issuable-details{ data: { id: @merge_request.project.id } } .merge-request-details.issuable-details{ data: { id: @merge_request.project.id } }
@ -15,13 +15,13 @@
- if @merge_request.source_branch_exists? - if @merge_request.source_branch_exists?
= render "projects/merge_requests/how_to_merge" = render "projects/merge_requests/how_to_merge"
-# haml-lint:disable InlineJavaScript
:javascript :javascript
window.gl.mrWidgetData = #{serialize_issuable(@merge_request)} window.gl.mrWidgetData = #{serialize_issuable(@merge_request)}
#js-vue-mr-widget.mr-widget #js-vue-mr-widget.mr-widget
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'vue_merge_request_widget' = webpack_bundle_tag 'vue_merge_request_widget'
.content-block.content-block-small.emoji-list-container .content-block.content-block-small.emoji-list-container
@ -88,10 +88,3 @@
= render "projects/commit/change", type: 'revert', commit: @merge_request.merge_commit, title: @merge_request.title = render "projects/commit/change", type: 'revert', commit: @merge_request.merge_commit, title: @merge_request.title
- if @merge_request.can_be_cherry_picked? - if @merge_request.can_be_cherry_picked?
= render "projects/commit/change", type: 'cherry-pick', commit: @merge_request.merge_commit, title: @merge_request.title = render "projects/commit/change", type: 'cherry-pick', commit: @merge_request.merge_commit, title: @merge_request.title
:javascript
$(function () {
window.mergeRequest = new MergeRequest({
action: "#{j params[:tab].presence || 'show'}",
});
});

View File

@ -72,7 +72,7 @@
%div %div
- if fogbugz_import_enabled? - if fogbugz_import_enabled?
= link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
= icon('bug', text: 'Fogbugz') = icon('bug', text: 'FogBugz')
%div %div
- if gitea_import_enabled? - if gitea_import_enabled?
= link_to new_import_gitea_url, class: 'btn import_gitea' do = link_to new_import_gitea_url, class: 'btn import_gitea' do

View File

@ -1,15 +1,11 @@
- @no_container = true - @no_container = true
- breadcrumb_title "Project" - breadcrumb_title "Project"
- @content_class = "limit-container-width" unless fluid_layout - @content_class = "limit-container-width" unless fluid_layout
- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity") = auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity")
= content_for flash_message_container do = render partial: 'flash_messages', locals: { project: @project }
- if current_user && can?(current_user, :download_code, @project)
= render 'shared/no_ssh'
= render 'shared/no_password'
= render "projects/head" = render "projects/head"
= render "projects/last_push" = render "projects/last_push"

View File

@ -1,6 +1,6 @@
- if @projects.any? - if @projects.any?
.project-item-select-holder .project-item-select-holder
= project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at' }, with_feature_enabled: local_assigns[:with_feature_enabled] = project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path] }, with_feature_enabled: local_assigns[:with_feature_enabled]
%a.btn.btn-new.new-project-item-select-button{ data: { relative_path: local_assigns[:path] } } %a.btn.btn-new.new-project-item-select-button
= local_assigns[:label] = local_assigns[:label]
= icon('caret-down') = icon('caret-down')

View File

@ -0,0 +1,3 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14">
<path d="M13 12.75v-8.5q0-0.102-0.074-0.176t-0.176-0.074h-8.5q-0.102 0-0.176 0.074t-0.074 0.176v8.5q0 0.102 0.074 0.176t0.176 0.074h8.5q0.102 0 0.176-0.074t0.074-0.176zM14 4.25v8.5q0 0.516-0.367 0.883t-0.883 0.367h-8.5q-0.516 0-0.883-0.367t-0.367-0.883v-8.5q0-0.516 0.367-0.883t0.883-0.367h8.5q0.516 0 0.883 0.367t0.367 0.883zM11 1.25v1.25h-1v-1.25q0-0.102-0.074-0.176t-0.176-0.074h-8.5q-0.102 0-0.176 0.074t-0.074 0.176v8.5q0 0.102 0.074 0.176t0.176 0.074h1.25v1h-1.25q-0.516 0-0.883-0.367t-0.367-0.883v-8.5q0-0.516 0.367-0.883t0.883-0.367h8.5q0.516 0 0.883 0.367t0.367 0.883z"></path>
</svg>

After

Width:  |  Height:  |  Size: 735 B

View File

@ -4,5 +4,5 @@
= link_to user, title: user.name, class: "darken" do = link_to user, title: user.name, class: "darken" do
= image_tag avatar_icon(user, 32), class: "avatar s32" = image_tag avatar_icon(user, 32), class: "avatar s32"
%strong= truncate(user.name, length: 40) %strong= truncate(user.name, length: 40)
%br %div
%small.cgray= user.username %small.cgray= user.username

View File

@ -6,15 +6,12 @@ class PipelineScheduleWorker
Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now) Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now)
.preload(:owner, :project).find_each do |schedule| .preload(:owner, :project).find_each do |schedule|
begin begin
unless schedule.runnable_by_owner? pipeline = Ci::CreatePipelineService.new(schedule.project,
schedule.deactivate! schedule.owner,
next ref: schedule.ref)
end
Ci::CreatePipelineService.new(schedule.project,
schedule.owner,
ref: schedule.ref)
.execute(:schedule, save_on_errors: false, schedule: schedule) .execute(:schedule, save_on_errors: false, schedule: schedule)
schedule.deactivate! unless pipeline.persisted?
rescue => e rescue => e
Rails.logger.error "#{schedule.id}: Failed to create a scheduled pipeline: #{e.message}" Rails.logger.error "#{schedule.id}: Failed to create a scheduled pipeline: #{e.message}"
ensure ensure

View File

@ -3,14 +3,11 @@ class ProjectDestroyWorker
include DedicatedSidekiqQueue include DedicatedSidekiqQueue
def perform(project_id, user_id, params) def perform(project_id, user_id, params)
begin project = Project.find(project_id)
project = Project.unscoped.find(project_id)
rescue ActiveRecord::RecordNotFound
return
end
user = User.find(user_id) user = User.find(user_id)
::Projects::DestroyService.new(project, user, params.symbolize_keys).execute ::Projects::DestroyService.new(project, user, params.symbolize_keys).execute
rescue ActiveRecord::RecordNotFound => error
logger.error("Failed to delete project (#{project_id}): #{error.message}")
end end
end end

View File

@ -0,0 +1,4 @@
---
title: Declare related resources into V4 API entities
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Added /duplicate quick action to close a duplicate issue
merge_request: 12845
author: Ryan Scott

View File

@ -0,0 +1,4 @@
---
title: Handle errors while a project is being deleted asynchronously.
merge_request: 11088
author:

View File

@ -0,0 +1,4 @@
---
title: Fix vertical alignment in firefox and safari for pipeline mini graph
merge_request:
author:

View File

@ -0,0 +1,5 @@
---
title: Disallow running the pipeline if ref is protected and user cannot merge the
branch or create the tag
merge_request: 11910
author:

View File

@ -0,0 +1,4 @@
---
title: Remove project_key from the Jira configuration
merge_request: 12050
author:

View File

@ -0,0 +1,4 @@
---
title: Add CSRF token verification to API
merge_request: 12154
author: Vitaliy @blackst0ne Klachkov

View File

@ -0,0 +1,4 @@
---
title: Added link to the MR widget that directs to the monitoring dashboard
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Remove help message about prioritized labels for non-members
merge_request: 12912
author: Takuya Noguchi

View File

@ -0,0 +1,4 @@
---
title: Add link to doc/api/ci/lint.md
merge_request: 12914
author: Takuya Noguchi

View File

@ -0,0 +1,4 @@
---
title: Pending delete projects should not show in deploy keys.
merge_request: 13088
author:

View File

@ -1,4 +0,0 @@
---
title: Fix margins in the mini graph for pipeline in commits box
merge_request:
author:

View File

@ -1,4 +0,0 @@
---
title: Fix anonymous access to public projects in groups with pending invites
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Fixes 500 error caused by pending delete projects in admin dashboard
merge_request: 13067
author:

View File

@ -0,0 +1,4 @@
---
title: Allow admin to read_users_list even if it's restricted
merge_request: 13066
author:

View File

@ -0,0 +1,5 @@
---
title: Fix creating merge request diffs when diff contains bytes that are invalid
in UTF-8
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Add instrumentation to MarkupHelper#link_to_gfm
merge_request: 13069
author:

View File

@ -0,0 +1,4 @@
---
title: Prevent LDAP login callback from being called with a GET request
merge_request: 13059
author:

View File

@ -1,4 +0,0 @@
---
title: Fix some invalid entries in PO files
merge_request: 13032
author:

View File

@ -1,5 +0,0 @@
---
title: Fix cross site request protection when logging in as a regular user when LDAP
is enabled
merge_request: 13049
author:

View File

@ -0,0 +1,5 @@
---
title: Free up some top level words, reject top level groups named like files in the
public folder
merge_request: 12932
author:

View File

@ -0,0 +1,4 @@
---
title: Support custom directory in gitlab:backup:create task
merge_request: 12984
author: Markus Koller

View File

@ -0,0 +1,4 @@
---
title: Fix job merge request link to a forked source project
merge_request: 12965
author:

View File

@ -0,0 +1,4 @@
---
title: Handle maximum pages artifacts size correctly
merge_request: 13072
author:

View File

@ -1,4 +0,0 @@
---
title: Fix editing project with container images present
merge_request: 13028
author:

View File

@ -1,4 +0,0 @@
---
title: Fix pipeline_schedules pages throwing error 500 (when ref is empty)
merge_request: 12983
author:

View File

@ -1,4 +0,0 @@
---
title: Fixed issue boards sidebar close icon size
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Add LDAP SSL certificate verification option
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Improve redirect route query performance
merge_request: 13062
author:

View File

@ -1,4 +0,0 @@
---
title: Fixed duplicate new milestone buttons when new navigation is turned on
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Fix sizing of custom header logo in new navigation
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Ensure filesystem metrics test files are deleted
merge_request:
author:

View File

@ -1,4 +0,0 @@
---
title: Fix bug causing metrics files to be truncated
merge_request: 35420
author:

View File

@ -0,0 +1,4 @@
---
title: Enable gitaly_post_upload_pack by default
merge_request: 13078
author:

View File

@ -0,0 +1,5 @@
---
title: Fix the /projects/:id/repository/branches endpoint to handle dots in the branch
name when the project full patch contains a `/`
merge_request: 13115
author:

View File

@ -0,0 +1,4 @@
---
title: Add author_id & assignee_id param to /issues API
merge_request: 13004
author:

View File

@ -228,7 +228,8 @@ production: &base
# ========================== # ==========================
## LDAP settings ## LDAP settings
# You can inspect a sample of the LDAP users with login access by running: # You can test connections and inspect a sample of the LDAP users with login
# access by running:
# bundle exec rake gitlab:ldap:check RAILS_ENV=production # bundle exec rake gitlab:ldap:check RAILS_ENV=production
ldap: ldap:
enabled: false enabled: false
@ -251,13 +252,45 @@ production: &base
# Example: 'Paris' or 'Acme, Ltd.' # Example: 'Paris' or 'Acme, Ltd.'
label: 'LDAP' label: 'LDAP'
# Example: 'ldap.mydomain.com'
host: '_your_ldap_server' host: '_your_ldap_server'
port: 389 # This port is an example, it is sometimes different but it is always an integer and not a string
uid: 'sAMAccountName' port: 389 # usually 636 for SSL
method: 'plain' # "tls" or "ssl" or "plain" uid: 'sAMAccountName' # This should be the attribute, not the value that maps to uid.
# Examples: 'america\\momo' or 'CN=Gitlab Git,CN=Users,DC=mydomain,DC=com'
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user' password: '_the_password_of_the_bind_user'
# Encryption method. The "method" key is deprecated in favor of
# "encryption".
#
# Examples: "start_tls" or "simple_tls" or "plain"
#
# Deprecated values: "tls" was replaced with "start_tls" and "ssl" was
# replaced with "simple_tls".
#
encryption: 'plain'
# Enables SSL certificate verification if encryption method is
# "start_tls" or "simple_tls". (Defaults to false for backward-
# compatibility)
verify_certificates: false
# Specifies the path to a file containing a PEM-format CA certificate,
# e.g. if you need to use an internal CA.
#
# Example: '/etc/ca.pem'
#
ca_cert: ''
# Specifies the SSL version for OpenSSL to use, if the OpenSSL default
# is not appropriate.
#
# Example: 'TLSv1_1'
#
ssl_version: ''
# Set a timeout, in seconds, for LDAP queries. This helps avoid blocking # Set a timeout, in seconds, for LDAP queries. This helps avoid blocking
# a request if the LDAP server becomes unresponsive. # a request if the LDAP server becomes unresponsive.
# A value of 0 means there is no timeout. # A value of 0 means there is no timeout.
@ -286,17 +319,20 @@ production: &base
# Base where we can search for users # Base where we can search for users
# #
# Ex. ou=People,dc=gitlab,dc=example # Ex. 'ou=People,dc=gitlab,dc=example' or 'DC=mydomain,DC=com'
# #
base: '' base: ''
# Filter LDAP users # Filter LDAP users
# #
# Format: RFC 4515 http://tools.ietf.org/search/rfc4515 # Format: RFC 4515 https://tools.ietf.org/search/rfc4515
# Ex. (employeeType=developer) # Ex. (employeeType=developer)
# #
# Note: GitLab does not support omniauth-ldap's custom filter syntax. # Note: GitLab does not support omniauth-ldap's custom filter syntax.
# #
# Example for getting only specific users:
# '(&(objectclass=user)(|(samaccountname=momo)(samaccountname=toto)))'
#
user_filter: '' user_filter: ''
# LDAP attributes that GitLab will use to create an account for the LDAP user. # LDAP attributes that GitLab will use to create an account for the LDAP user.
@ -674,7 +710,7 @@ test:
host: 127.0.0.1 host: 127.0.0.1
port: 3890 port: 3890
uid: 'uid' uid: 'uid'
method: 'plain' # "tls" or "ssl" or "plain" encryption: 'plain' # "start_tls" or "simple_tls" or "plain"
base: 'dc=example,dc=com' base: 'dc=example,dc=com'
user_filter: '' user_filter: ''
group_base: 'ou=groups,dc=example,dc=com' group_base: 'ou=groups,dc=example,dc=com'

View File

@ -145,6 +145,24 @@ if Settings.ldap['enabled'] || Rails.env.test?
server['attributes'] = {} if server['attributes'].nil? server['attributes'] = {} if server['attributes'].nil?
server['provider_name'] ||= "ldap#{key}".downcase server['provider_name'] ||= "ldap#{key}".downcase
server['provider_class'] = OmniAuth::Utils.camelize(server['provider_name']) server['provider_class'] = OmniAuth::Utils.camelize(server['provider_name'])
# For backwards compatibility
server['encryption'] ||= server['method']
server['encryption'] = 'simple_tls' if server['encryption'] == 'ssl'
server['encryption'] = 'start_tls' if server['encryption'] == 'tls'
# Certificates are not verified for backwards compatibility.
# This default should be flipped to true in 9.5.
if server['verify_certificates'].nil?
server['verify_certificates'] = false
message = <<-MSG.strip_heredoc
LDAP SSL certificate verification is disabled for backwards-compatibility.
Please add the "verify_certificates" option to gitlab.yml for each LDAP
server. Certificate verification will be enabled by default in GitLab 9.5.
MSG
Rails.logger.warn(message)
end
end end
end end
@ -441,10 +459,6 @@ Settings.backup['pg_schema'] = nil
Settings.backup['path'] = Settings.absolute(Settings.backup['path'] || "tmp/backups/") Settings.backup['path'] = Settings.absolute(Settings.backup['path'] || "tmp/backups/")
Settings.backup['archive_permissions'] ||= 0600 Settings.backup['archive_permissions'] ||= 0600
Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil }) Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil })
# Convert upload connection settings to use symbol keys, to make Fog happy
if Settings.backup['upload']['connection']
Settings.backup['upload']['connection'] = Hash[Settings.backup['upload']['connection'].map { |k, v| [k.to_sym, v] }]
end
Settings.backup['upload']['multipart_chunk_size'] ||= 104857600 Settings.backup['upload']['multipart_chunk_size'] ||= 104857600
Settings.backup['upload']['encryption'] ||= nil Settings.backup['upload']['encryption'] ||= nil
Settings.backup['upload']['storage_class'] ||= nil Settings.backup['upload']['storage_class'] ||= nil

View File

@ -6,7 +6,7 @@ Prometheus::Client.configure do |config|
config.initial_mmap_file_size = 4 * 1024 config.initial_mmap_file_size = 4 * 1024
config.multiprocess_files_dir = ENV['prometheus_multiproc_dir'] config.multiprocess_files_dir = ENV['prometheus_multiproc_dir']
if Rails.env.development? && Rails.env.test? if Rails.env.development? || Rails.env.test?
config.multiprocess_files_dir ||= Rails.root.join('tmp/prometheus_multiproc_dir') config.multiprocess_files_dir ||= Rails.root.join('tmp/prometheus_multiproc_dir')
end end
end end

View File

@ -114,6 +114,9 @@ def instrument_classes(instrumentation)
# This is a Rails scope so we have to instrument it manually. # This is a Rails scope so we have to instrument it manually.
instrumentation.instrument_method(Project, :visible_to_user) instrumentation.instrument_method(Project, :visible_to_user)
# Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/34509
instrumentation.instrument_method(MarkupHelper, :link_to_gfm)
# Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/30224#note_32306159 # Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/30224#note_32306159
instrumentation.instrument_instance_method(MergeRequestDiff, :load_commits) instrumentation.instrument_instance_method(MergeRequestDiff, :load_commits)
end end

View File

@ -0,0 +1,35 @@
if defined?(GrapeRouteHelpers)
module GrapeRouteHelpers
class DecoratedRoute
# GrapeRouteHelpers gem tries to parse the versions
# from a string, not supporting Grape `version` array definition.
#
# Without the following fix, we get this on route helpers generation:
#
# => undefined method `scan' for ["v3", "v4"]
#
# 2.0.0 implementation of this method:
#
# ```
# def route_versions
# version_pattern = /[^\[",\]\s]+/
# if route_version
# route_version.scan(version_pattern)
# else
# [nil]
# end
# end
# ```
def route_versions
return [nil] if route_version.nil? || route_version.empty?
if route_version.is_a?(String)
version_pattern = /[^\[",\]\s]+/
route_version.scan(version_pattern)
else
route_version
end
end
end
end
end

View File

@ -16,7 +16,7 @@ OmniAuth.config.allowed_request_methods = [:post]
# In case of auto sign-in, the GET method is used (users don't get to click on a button) # In case of auto sign-in, the GET method is used (users don't get to click on a button)
OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present? OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present?
OmniAuth.config.before_request_phase do |env| OmniAuth.config.before_request_phase do |env|
OmniAuth::RequestForgeryProtection.call(env) Gitlab::RequestForgeryProtection.call(env)
end end
if Gitlab.config.omniauth.enabled if Gitlab.config.omniauth.enabled

Some files were not shown because too many files have changed in this diff Show More