Merge branch 'master' into fixes/api
This commit is contained in:
commit
ac4a09e9cc
94 changed files with 935 additions and 731 deletions
|
@ -31,7 +31,7 @@ class MergeRequest
|
|||
|
||||
if this.$('.automerge_widget').length and @opts.check_enable
|
||||
$.get @opts.url_to_automerge_check, (data) =>
|
||||
this.showState( data.state )
|
||||
this.showState( data.merge_status )
|
||||
, 'json'
|
||||
|
||||
if @opts.ci_enable
|
||||
|
|
|
@ -24,6 +24,14 @@
|
|||
background-image: -o-linear-gradient($from, $to);
|
||||
}
|
||||
|
||||
@mixin transition($transition) {
|
||||
-webkit-transition: $transition;
|
||||
-moz-transition: $transition;
|
||||
-ms-transition: $transition;
|
||||
-o-transition: $transition;
|
||||
transition: $transition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefilled mixins
|
||||
* Mixins with fixed values
|
||||
|
|
|
@ -90,6 +90,7 @@ header {
|
|||
@include border-radius(3px);
|
||||
border: 1px solid #c6c6c6;
|
||||
box-shadow: none;
|
||||
@include transition(all 0.15s ease-in 0s);
|
||||
&:focus {
|
||||
@extend .span3;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext
|
|||
end
|
||||
|
||||
merge_requests = merge_requests.page(params[:page]).per(20)
|
||||
merge_requests = merge_requests.includes(:author, :project).order("state, created_at desc")
|
||||
merge_requests = merge_requests.includes(:author, :project).order("created_at desc")
|
||||
|
||||
# Filter by specific assignee_id (or lack thereof)?
|
||||
if params[:assignee_id].present?
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
class TestHookContext < BaseContext
|
||||
def execute
|
||||
hook = project.hooks.find(params[:id])
|
||||
commits = project.repository.commits(project.default_branch, nil, 3)
|
||||
data = project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", current_user)
|
||||
data = GitPushService.new.sample_data(project, current_user)
|
||||
hook.execute(data)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'gitlab/satellite/satellite'
|
||||
|
||||
class MergeRequestsController < ProjectResourceController
|
||||
before_filter :module_enabled
|
||||
before_filter :merge_request, only: [:edit, :update, :show, :commits, :diffs, :automerge, :automerge_check, :ci_status]
|
||||
|
@ -73,7 +75,7 @@ class MergeRequestsController < ProjectResourceController
|
|||
if @merge_request.unchecked?
|
||||
@merge_request.check_if_can_be_merged
|
||||
end
|
||||
render json: {merge_status: @merge_request.human_merge_status}
|
||||
render json: {merge_status: @merge_request.merge_status_name}
|
||||
rescue Gitlab::SatelliteNotExistError
|
||||
render json: {merge_status: :no_satellite}
|
||||
end
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
class ProfilesController < ApplicationController
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
|
||||
before_filter :user
|
||||
layout 'profile'
|
||||
|
||||
|
@ -12,7 +14,7 @@ class ProfilesController < ApplicationController
|
|||
end
|
||||
|
||||
def update
|
||||
if @user.update_attributes(params[:user])
|
||||
if @user.update_attributes(user_attributes)
|
||||
flash[:notice] = "Profile was successfully updated"
|
||||
else
|
||||
flash[:alert] = "Failed to update profile"
|
||||
|
@ -65,4 +67,17 @@ class ProfilesController < ApplicationController
|
|||
def user
|
||||
@user = current_user
|
||||
end
|
||||
|
||||
def user_attributes
|
||||
user_attributes = params[:user]
|
||||
|
||||
# Sanitize user input because we dont have strict
|
||||
# validation for this fields
|
||||
%w(name skype linkedin twitter bio).each do |attr|
|
||||
value = user_attributes[attr]
|
||||
user_attributes[attr] = sanitize(value) if value.present?
|
||||
end
|
||||
|
||||
user_attributes
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,7 +72,7 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def search_autocomplete_source
|
||||
projects = current_user.authorized_projects.map { |p| { label: "project: #{p.name_with_namespace}", url: project_path(p) } }
|
||||
projects = current_user.authorized_projects.map { |p| { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } }
|
||||
groups = current_user.authorized_groups.map { |group| { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) } }
|
||||
teams = current_user.authorized_teams.map { |team| { label: "team: #{simple_sanitize(team.name)}", url: team_path(team) } }
|
||||
|
||||
|
@ -98,15 +98,15 @@ module ApplicationHelper
|
|||
project_nav = []
|
||||
if @project && @project.repository && @project.repository.root_ref
|
||||
project_nav = [
|
||||
{ label: "#{@project.name_with_namespace} - Issues", url: project_issues_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
|
||||
{ label: "#{@project.name_with_namespace} - Merge Requests", url: project_merge_requests_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Milestones", url: project_milestones_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Snippets", url: project_snippets_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Team", url: project_team_index_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) },
|
||||
{ label: "#{@project.name_with_namespace} - Wall", url: wall_project_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Wiki", url: project_wikis_path(@project) },
|
||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) },
|
||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
|
||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Merge Requests", url: project_merge_requests_path(@project) },
|
||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Milestones", url: project_milestones_path(@project) },
|
||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Snippets", url: project_snippets_path(@project) },
|
||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Team", url: project_team_index_path(@project) },
|
||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) },
|
||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Wall", url: wall_project_path(@project) },
|
||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Wiki", url: project_wikis_path(@project) },
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -57,6 +57,31 @@ module CommitsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def each_diff_line_near(diff, index, expected_line_code)
|
||||
max_number_of_lines = 16
|
||||
|
||||
prev_match_line = nil
|
||||
prev_lines = []
|
||||
|
||||
each_diff_line(diff, index) do |full_line, type, line_code, line_new, line_old|
|
||||
line = [full_line, type, line_code, line_new, line_old]
|
||||
if line_code != expected_line_code
|
||||
if type == "match"
|
||||
prev_lines.clear
|
||||
prev_match_line = line
|
||||
else
|
||||
prev_lines.push(line)
|
||||
prev_lines.shift if prev_lines.length >= max_number_of_lines
|
||||
end
|
||||
else
|
||||
yield(prev_match_line) if !prev_match_line.nil?
|
||||
prev_lines.each { |ln| yield(ln) }
|
||||
yield(line)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def image_diff_class(diff)
|
||||
if diff.deleted_file
|
||||
"deleted"
|
||||
|
|
|
@ -21,7 +21,6 @@ class Key < ActiveRecord::Base
|
|||
attr_accessible :key, :title
|
||||
|
||||
before_validation :strip_white_space
|
||||
before_save :set_identifier
|
||||
|
||||
validates :title, presence: true, length: { within: 0..255 }
|
||||
validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }, uniqueness: true
|
||||
|
@ -48,14 +47,6 @@ class Key < ActiveRecord::Base
|
|||
errors.add(:key, "can't be fingerprinted") if $?.exitstatus != 0
|
||||
end
|
||||
|
||||
def set_identifier
|
||||
if is_deploy_key
|
||||
self.identifier = "deploy_#{Digest::MD5.hexdigest(key)}"
|
||||
else
|
||||
self.identifier = "#{user.identifier}_#{Time.now.to_i}"
|
||||
end
|
||||
end
|
||||
|
||||
def is_deploy_key
|
||||
!!project_id
|
||||
end
|
||||
|
|
|
@ -24,6 +24,8 @@ require Rails.root.join("lib/static_model")
|
|||
class MergeRequest < ActiveRecord::Base
|
||||
include Issuable
|
||||
|
||||
BROKEN_DIFF = "--broken-diff"
|
||||
|
||||
attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id,
|
||||
:author_id_of_changes, :state_event
|
||||
|
||||
|
@ -51,47 +53,41 @@ class MergeRequest < ActiveRecord::Base
|
|||
state :merged
|
||||
end
|
||||
|
||||
BROKEN_DIFF = "--broken-diff"
|
||||
state_machine :merge_status, initial: :unchecked do
|
||||
event :mark_as_unchecked do
|
||||
transition [:can_be_merged, :cannot_be_merged] => :unchecked
|
||||
end
|
||||
|
||||
UNCHECKED = 1
|
||||
CAN_BE_MERGED = 2
|
||||
CANNOT_BE_MERGED = 3
|
||||
event :mark_as_mergeable do
|
||||
transition unchecked: :can_be_merged
|
||||
end
|
||||
|
||||
event :mark_as_unmergeable do
|
||||
transition unchecked: :cannot_be_merged
|
||||
end
|
||||
|
||||
state :unchecked
|
||||
|
||||
state :can_be_merged
|
||||
|
||||
state :cannot_be_merged
|
||||
end
|
||||
|
||||
serialize :st_commits
|
||||
serialize :st_diffs
|
||||
|
||||
validates :source_branch, presence: true
|
||||
validates :target_branch, presence: true
|
||||
validate :validate_branches
|
||||
validate :validate_branches
|
||||
|
||||
scope :merged, -> { with_state(:merged) }
|
||||
scope :by_branch, ->(branch_name) { where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name) }
|
||||
scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
|
||||
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
|
||||
|
||||
class << self
|
||||
def find_all_by_branch(branch_name)
|
||||
where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
|
||||
end
|
||||
|
||||
def cared(user)
|
||||
where('assignee_id = :user OR author_id = :user', user: user.id)
|
||||
end
|
||||
|
||||
def find_all_by_branch(branch_name)
|
||||
where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
|
||||
end
|
||||
|
||||
def find_all_by_milestone(milestone)
|
||||
where("milestone_id = :milestone_id", milestone_id: milestone)
|
||||
end
|
||||
end
|
||||
|
||||
def human_merge_status
|
||||
merge_statuses = {
|
||||
CAN_BE_MERGED => "can_be_merged",
|
||||
CANNOT_BE_MERGED => "cannot_be_merged",
|
||||
UNCHECKED => "unchecked"
|
||||
}
|
||||
merge_statuses[self.merge_status]
|
||||
end
|
||||
# Closed scope for merge request should return
|
||||
# both merged and closed mr's
|
||||
scope :closed, -> { with_states(:closed, :merged) }
|
||||
|
||||
def validate_branches
|
||||
if target_branch == source_branch
|
||||
|
@ -104,26 +100,12 @@ class MergeRequest < ActiveRecord::Base
|
|||
self.reloaded_diffs
|
||||
end
|
||||
|
||||
def unchecked?
|
||||
merge_status == UNCHECKED
|
||||
end
|
||||
|
||||
def mark_as_unchecked
|
||||
self.merge_status = UNCHECKED
|
||||
self.save
|
||||
end
|
||||
|
||||
def can_be_merged?
|
||||
merge_status == CAN_BE_MERGED
|
||||
end
|
||||
|
||||
def check_if_can_be_merged
|
||||
self.merge_status = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
|
||||
CAN_BE_MERGED
|
||||
else
|
||||
CANNOT_BE_MERGED
|
||||
end
|
||||
self.save
|
||||
if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
|
||||
mark_as_mergeable
|
||||
else
|
||||
mark_as_unmergeable
|
||||
end
|
||||
end
|
||||
|
||||
def diffs
|
||||
|
@ -178,11 +160,6 @@ class MergeRequest < ActiveRecord::Base
|
|||
commits.any? && opened?
|
||||
end
|
||||
|
||||
def mark_as_unmergable
|
||||
self.merge_status = CANNOT_BE_MERGED
|
||||
self.save
|
||||
end
|
||||
|
||||
def reloaded_commits
|
||||
if opened? && unmerged_commits.any?
|
||||
self.st_commits = unmerged_commits
|
||||
|
@ -217,7 +194,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
true
|
||||
end
|
||||
rescue
|
||||
self.mark_as_unmergable
|
||||
mark_as_unmergeable
|
||||
false
|
||||
end
|
||||
|
||||
|
|
|
@ -247,32 +247,6 @@ class Project < ActiveRecord::Base
|
|||
users_projects.find_by_user_id(user_id)
|
||||
end
|
||||
|
||||
def transfer(new_namespace)
|
||||
Project.transaction do
|
||||
old_namespace = namespace
|
||||
self.namespace = new_namespace
|
||||
|
||||
old_dir = old_namespace.try(:path) || ''
|
||||
new_dir = new_namespace.try(:path) || ''
|
||||
|
||||
old_repo = if old_dir.present?
|
||||
File.join(old_dir, self.path)
|
||||
else
|
||||
self.path
|
||||
end
|
||||
|
||||
if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present?
|
||||
raise TransferError.new("Project with same path in target namespace already exists")
|
||||
end
|
||||
|
||||
Gitlab::ProjectMover.new(self, old_dir, new_dir).execute
|
||||
|
||||
save!
|
||||
end
|
||||
rescue Gitlab::ProjectMover::ProjectMoveError => ex
|
||||
raise Project::TransferError.new(ex.message)
|
||||
end
|
||||
|
||||
def name_with_namespace
|
||||
@name_with_namespace ||= begin
|
||||
if namespace
|
||||
|
@ -295,51 +269,8 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
# This method will be called after each post receive and only if the provided
|
||||
# user is present in GitLab.
|
||||
#
|
||||
# All callbacks for post receive should be placed here.
|
||||
def trigger_post_receive(oldrev, newrev, ref, user)
|
||||
data = post_receive_data(oldrev, newrev, ref, user)
|
||||
|
||||
# Create satellite
|
||||
self.satellite.create unless self.satellite.exists?
|
||||
|
||||
# Create push event
|
||||
self.observe_push(data)
|
||||
|
||||
if push_to_branch? ref, oldrev
|
||||
# Close merged MR
|
||||
self.update_merge_requests(oldrev, newrev, ref, user)
|
||||
|
||||
# Execute web hooks
|
||||
self.execute_hooks(data.dup)
|
||||
|
||||
# Execute project services
|
||||
self.execute_services(data.dup)
|
||||
end
|
||||
|
||||
# Discover the default branch, but only if it hasn't already been set to
|
||||
# something else
|
||||
if repository && default_branch.nil?
|
||||
update_attributes(default_branch: self.repository.discover_default_branch)
|
||||
end
|
||||
end
|
||||
|
||||
def push_to_branch? ref, oldrev
|
||||
ref_parts = ref.split('/')
|
||||
|
||||
# Return if this is not a push to a branch (e.g. new commits)
|
||||
!(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000")
|
||||
end
|
||||
|
||||
def observe_push(data)
|
||||
Event.create(
|
||||
project: self,
|
||||
action: Event::PUSHED,
|
||||
data: data,
|
||||
author_id: data[:user_id]
|
||||
)
|
||||
def transfer(new_namespace)
|
||||
ProjectTransferService.new.transfer(self, new_namespace)
|
||||
end
|
||||
|
||||
def execute_hooks(data)
|
||||
|
@ -354,68 +285,12 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
# Produce a hash of post-receive data
|
||||
#
|
||||
# data = {
|
||||
# before: String,
|
||||
# after: String,
|
||||
# ref: String,
|
||||
# user_id: String,
|
||||
# user_name: String,
|
||||
# repository: {
|
||||
# name: String,
|
||||
# url: String,
|
||||
# description: String,
|
||||
# homepage: String,
|
||||
# },
|
||||
# commits: Array,
|
||||
# total_commits_count: Fixnum
|
||||
# }
|
||||
#
|
||||
def post_receive_data(oldrev, newrev, ref, user)
|
||||
|
||||
push_commits = repository.commits_between(oldrev, newrev)
|
||||
|
||||
# Total commits count
|
||||
push_commits_count = push_commits.size
|
||||
|
||||
# Get latest 20 commits ASC
|
||||
push_commits_limited = push_commits.last(20)
|
||||
|
||||
# Hash to be passed as post_receive_data
|
||||
data = {
|
||||
before: oldrev,
|
||||
after: newrev,
|
||||
ref: ref,
|
||||
user_id: user.id,
|
||||
user_name: user.name,
|
||||
repository: {
|
||||
name: name,
|
||||
url: url_to_repo,
|
||||
description: description,
|
||||
homepage: web_url,
|
||||
},
|
||||
commits: [],
|
||||
total_commits_count: push_commits_count
|
||||
}
|
||||
|
||||
# For perfomance purposes maximum 20 latest commits
|
||||
# will be passed as post receive hook data.
|
||||
#
|
||||
push_commits_limited.each do |commit|
|
||||
data[:commits] << {
|
||||
id: commit.id,
|
||||
message: commit.safe_message,
|
||||
timestamp: commit.date.xmlschema,
|
||||
url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}",
|
||||
author: {
|
||||
name: commit.author_name,
|
||||
email: commit.author_email
|
||||
}
|
||||
}
|
||||
def discover_default_branch
|
||||
# Discover the default branch, but only if it hasn't already been set to
|
||||
# something else
|
||||
if repository && default_branch.nil?
|
||||
update_attributes(default_branch: self.repository.discover_default_branch)
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
def update_merge_requests(oldrev, newrev, ref, user)
|
||||
|
@ -424,7 +299,7 @@ class Project < ActiveRecord::Base
|
|||
c_ids = self.repository.commits_between(oldrev, newrev).map(&:id)
|
||||
|
||||
# Update code for merge requests
|
||||
mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all
|
||||
mrs = self.merge_requests.opened.by_branch(branch_name).all
|
||||
mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
|
||||
|
||||
# Close merge requests
|
||||
|
@ -446,6 +321,10 @@ class Project < ActiveRecord::Base
|
|||
!repository || repository.empty?
|
||||
end
|
||||
|
||||
def ensure_satellite_exists
|
||||
self.satellite.create unless self.satellite.exists?
|
||||
end
|
||||
|
||||
def satellite
|
||||
@satellite ||= Gitlab::Satellite::Satellite.new(self)
|
||||
end
|
||||
|
|
|
@ -66,28 +66,6 @@ class ProjectTeam
|
|||
members.masters.map(&:user)
|
||||
end
|
||||
|
||||
def repository_readers
|
||||
repository_members[UsersProject::REPORTER]
|
||||
end
|
||||
|
||||
def repository_writers
|
||||
repository_members[UsersProject::DEVELOPER]
|
||||
end
|
||||
|
||||
def repository_masters
|
||||
repository_members[UsersProject::MASTER]
|
||||
end
|
||||
|
||||
def repository_members
|
||||
keys = Hash.new {|h,k| h[k] = [] }
|
||||
UsersProject.select("keys.identifier, project_access").
|
||||
joins(user: :keys).where(project_id: project.id).
|
||||
each {|row| keys[row.project_access] << [row.identifier] }
|
||||
|
||||
keys[UsersProject::REPORTER] += project.deploy_keys.pluck(:identifier)
|
||||
keys
|
||||
end
|
||||
|
||||
def import(source_project)
|
||||
target_project = project
|
||||
|
||||
|
|
|
@ -12,13 +12,4 @@
|
|||
#
|
||||
|
||||
class SystemHook < WebHook
|
||||
def self.all_hooks_fire(data)
|
||||
SystemHook.all.each do |sh|
|
||||
sh.async_execute data
|
||||
end
|
||||
end
|
||||
|
||||
def async_execute(data)
|
||||
Sidekiq::Client.enqueue(SystemHookWorker, id, data)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -70,6 +70,7 @@ class User < ActiveRecord::Base
|
|||
has_many :team_projects, through: :user_team_project_relationships
|
||||
|
||||
validates :name, presence: true
|
||||
validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
|
||||
validates :bio, length: { within: 0..255 }
|
||||
validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
|
||||
validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
|
||||
|
@ -215,17 +216,6 @@ class User < ActiveRecord::Base
|
|||
UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id)
|
||||
end
|
||||
|
||||
# Returns a string for use as a Gitolite user identifier
|
||||
#
|
||||
# Note that Gitolite 2.x requires the following pattern for users:
|
||||
#
|
||||
# ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$
|
||||
def identifier
|
||||
# Replace non-word chars with underscores, then make sure it starts with
|
||||
# valid chars
|
||||
email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '')
|
||||
end
|
||||
|
||||
def is_admin?
|
||||
admin
|
||||
end
|
||||
|
|
|
@ -28,10 +28,14 @@ class WebHook < ActiveRecord::Base
|
|||
WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" })
|
||||
else
|
||||
post_url = url.gsub("#{parsed_url.userinfo}@", "")
|
||||
auth = {
|
||||
username: URI.decode(parsed_url.user),
|
||||
password: URI.decode(parsed_url.password),
|
||||
}
|
||||
WebHook.post(post_url,
|
||||
body: data.to_json,
|
||||
headers: {"Content-Type" => "application/json"},
|
||||
basic_auth: {username: parsed_url.user, password: parsed_url.password})
|
||||
basic_auth: auth)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -21,22 +21,22 @@ class ActivityObserver < ActiveRecord::Observer
|
|||
end
|
||||
|
||||
def after_close(record, transition)
|
||||
Event.create(
|
||||
project: record.project,
|
||||
target_id: record.id,
|
||||
target_type: record.class.name,
|
||||
action: Event::CLOSED,
|
||||
author_id: record.author_id_of_changes
|
||||
)
|
||||
Event.create(
|
||||
project: record.project,
|
||||
target_id: record.id,
|
||||
target_type: record.class.name,
|
||||
action: Event::CLOSED,
|
||||
author_id: record.author_id_of_changes
|
||||
)
|
||||
end
|
||||
|
||||
def after_reopen(record, transition)
|
||||
Event.create(
|
||||
project: record.project,
|
||||
target_id: record.id,
|
||||
target_type: record.class.name,
|
||||
action: Event::REOPENED,
|
||||
author_id: record.author_id_of_changes
|
||||
)
|
||||
Event.create(
|
||||
project: record.project,
|
||||
target_id: record.id,
|
||||
target_type: record.class.name,
|
||||
action: Event::REOPENED,
|
||||
author_id: record.author_id_of_changes
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
class NoteObserver < ActiveRecord::Observer
|
||||
|
||||
def after_create(note)
|
||||
send_notify_mails(note)
|
||||
end
|
||||
|
|
|
@ -1,67 +1,11 @@
|
|||
class SystemHookObserver < ActiveRecord::Observer
|
||||
observe :user, :project, :users_project
|
||||
|
||||
|
||||
def after_create(model)
|
||||
if model.kind_of? Project
|
||||
SystemHook.all_hooks_fire({
|
||||
event_name: "project_create",
|
||||
name: model.name,
|
||||
path: model.path,
|
||||
project_id: model.id,
|
||||
owner_name: model.owner.name,
|
||||
owner_email: model.owner.email,
|
||||
created_at: model.created_at
|
||||
})
|
||||
elsif model.kind_of? User
|
||||
SystemHook.all_hooks_fire({
|
||||
event_name: "user_create",
|
||||
name: model.name,
|
||||
email: model.email,
|
||||
created_at: model.created_at
|
||||
})
|
||||
|
||||
elsif model.kind_of? UsersProject
|
||||
SystemHook.all_hooks_fire({
|
||||
event_name: "user_add_to_team",
|
||||
project_name: model.project.name,
|
||||
project_path: model.project.path,
|
||||
project_id: model.project_id,
|
||||
user_name: model.user.name,
|
||||
user_email: model.user.email,
|
||||
project_access: model.repo_access_human,
|
||||
created_at: model.created_at
|
||||
})
|
||||
|
||||
end
|
||||
SystemHooksService.execute_hooks_for(model, :create)
|
||||
end
|
||||
|
||||
def after_destroy(model)
|
||||
if model.kind_of? Project
|
||||
SystemHook.all_hooks_fire({
|
||||
event_name: "project_destroy",
|
||||
name: model.name,
|
||||
path: model.path,
|
||||
project_id: model.id,
|
||||
owner_name: model.owner.name,
|
||||
owner_email: model.owner.email,
|
||||
})
|
||||
elsif model.kind_of? User
|
||||
SystemHook.all_hooks_fire({
|
||||
event_name: "user_destroy",
|
||||
name: model.name,
|
||||
email: model.email
|
||||
})
|
||||
|
||||
elsif model.kind_of? UsersProject
|
||||
SystemHook.all_hooks_fire({
|
||||
event_name: "user_remove_from_team",
|
||||
project_name: model.project.name,
|
||||
project_path: model.project.path,
|
||||
project_id: model.project_id,
|
||||
user_name: model.user.name,
|
||||
user_email: model.user.email,
|
||||
project_access: model.repo_access_human
|
||||
})
|
||||
end
|
||||
SystemHooksService.execute_hooks_for(model, :destroy)
|
||||
end
|
||||
end
|
||||
|
|
124
app/services/git_push_service.rb
Normal file
124
app/services/git_push_service.rb
Normal file
|
@ -0,0 +1,124 @@
|
|||
class GitPushService
|
||||
attr_accessor :project, :user, :push_data
|
||||
|
||||
# This method will be called after each git update
|
||||
# and only if the provided user and project is present in GitLab.
|
||||
#
|
||||
# All callbacks for post receive action should be placed here.
|
||||
#
|
||||
# Now this method do next:
|
||||
# 1. Ensure project satellite exists
|
||||
# 2. Update merge requests
|
||||
# 3. Execute project web hooks
|
||||
# 4. Execute project services
|
||||
# 5. Create Push Event
|
||||
#
|
||||
def execute(project, user, oldrev, newrev, ref)
|
||||
@project, @user = project, user
|
||||
|
||||
# Collect data for this git push
|
||||
@push_data = post_receive_data(oldrev, newrev, ref)
|
||||
|
||||
project.ensure_satellite_exists
|
||||
project.discover_default_branch
|
||||
|
||||
if push_to_branch?(ref, oldrev)
|
||||
project.update_merge_requests(oldrev, newrev, ref, @user)
|
||||
project.execute_hooks(@push_data.dup)
|
||||
project.execute_services(@push_data.dup)
|
||||
end
|
||||
|
||||
create_push_event
|
||||
end
|
||||
|
||||
# This method provide a sample data
|
||||
# generated with post_receive_data method
|
||||
# for given project
|
||||
#
|
||||
def sample_data(project, user)
|
||||
@project, @user = project, user
|
||||
commits = project.repository.commits(project.default_branch, nil, 3)
|
||||
post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}")
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def create_push_event
|
||||
Event.create(
|
||||
project: project,
|
||||
action: Event::PUSHED,
|
||||
data: push_data,
|
||||
author_id: push_data[:user_id]
|
||||
)
|
||||
end
|
||||
|
||||
# Produce a hash of post-receive data
|
||||
#
|
||||
# data = {
|
||||
# before: String,
|
||||
# after: String,
|
||||
# ref: String,
|
||||
# user_id: String,
|
||||
# user_name: String,
|
||||
# repository: {
|
||||
# name: String,
|
||||
# url: String,
|
||||
# description: String,
|
||||
# homepage: String,
|
||||
# },
|
||||
# commits: Array,
|
||||
# total_commits_count: Fixnum
|
||||
# }
|
||||
#
|
||||
def post_receive_data(oldrev, newrev, ref)
|
||||
push_commits = project.repository.commits_between(oldrev, newrev)
|
||||
|
||||
# Total commits count
|
||||
push_commits_count = push_commits.size
|
||||
|
||||
# Get latest 20 commits ASC
|
||||
push_commits_limited = push_commits.last(20)
|
||||
|
||||
# Hash to be passed as post_receive_data
|
||||
data = {
|
||||
before: oldrev,
|
||||
after: newrev,
|
||||
ref: ref,
|
||||
user_id: user.id,
|
||||
user_name: user.name,
|
||||
repository: {
|
||||
name: project.name,
|
||||
url: project.url_to_repo,
|
||||
description: project.description,
|
||||
homepage: project.web_url,
|
||||
},
|
||||
commits: [],
|
||||
total_commits_count: push_commits_count
|
||||
}
|
||||
|
||||
# For perfomance purposes maximum 20 latest commits
|
||||
# will be passed as post receive hook data.
|
||||
#
|
||||
push_commits_limited.each do |commit|
|
||||
data[:commits] << {
|
||||
id: commit.id,
|
||||
message: commit.safe_message,
|
||||
timestamp: commit.date.xmlschema,
|
||||
url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{commit.id}",
|
||||
author: {
|
||||
name: commit.author_name,
|
||||
email: commit.author_email
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
def push_to_branch? ref, oldrev
|
||||
ref_parts = ref.split('/')
|
||||
|
||||
# Return if this is not a push to a branch (e.g. new commits)
|
||||
!(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000")
|
||||
end
|
||||
end
|
34
app/services/project_transfer_service.rb
Normal file
34
app/services/project_transfer_service.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
# ProjectTransferService class
|
||||
#
|
||||
# Used for transfer project to another namespace
|
||||
#
|
||||
class ProjectTransferService
|
||||
attr_accessor :project
|
||||
|
||||
def transfer(project, new_namespace)
|
||||
Project.transaction do
|
||||
old_namespace = project.namespace
|
||||
project.namespace = new_namespace
|
||||
|
||||
old_dir = old_namespace.try(:path) || ''
|
||||
new_dir = new_namespace.try(:path) || ''
|
||||
|
||||
old_repo = if old_dir.present?
|
||||
File.join(old_dir, project.path)
|
||||
else
|
||||
project.path
|
||||
end
|
||||
|
||||
if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present?
|
||||
raise TransferError.new("Project with same path in target namespace already exists")
|
||||
end
|
||||
|
||||
Gitlab::ProjectMover.new(project, old_dir, new_dir).execute
|
||||
|
||||
save!
|
||||
end
|
||||
rescue Gitlab::ProjectMover::ProjectMoveError => ex
|
||||
raise Project::TransferError.new(ex.message)
|
||||
end
|
||||
end
|
||||
|
59
app/services/system_hooks_service.rb
Normal file
59
app/services/system_hooks_service.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
class SystemHooksService
|
||||
def self.execute_hooks_for(model, event)
|
||||
execute_hooks(build_event_data(model, event))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.execute_hooks(data)
|
||||
SystemHook.all.each do |sh|
|
||||
async_execute_hook sh, data
|
||||
end
|
||||
end
|
||||
|
||||
def self.async_execute_hook(hook, data)
|
||||
Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data)
|
||||
end
|
||||
|
||||
def self.build_event_data(model, event)
|
||||
data = {
|
||||
event_name: build_event_name(model, event),
|
||||
created_at: model.created_at
|
||||
}
|
||||
|
||||
case model
|
||||
when Project
|
||||
data.merge!({
|
||||
name: model.name,
|
||||
path: model.path,
|
||||
project_id: model.id,
|
||||
owner_name: model.owner.name,
|
||||
owner_email: model.owner.email
|
||||
})
|
||||
when User
|
||||
data.merge!({
|
||||
name: model.name,
|
||||
email: model.email
|
||||
})
|
||||
when UsersProject
|
||||
data.merge!({
|
||||
project_name: model.project.name,
|
||||
project_path: model.project.path,
|
||||
project_id: model.project_id,
|
||||
user_name: model.user.name,
|
||||
user_email: model.user.email,
|
||||
project_access: model.repo_access_human
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def self.build_event_name(model, event)
|
||||
case model
|
||||
when UsersProject
|
||||
return "user_add_to_team" if event == :create
|
||||
return "user_remove_from_team" if event == :destroy
|
||||
else
|
||||
"#{model.class.name.downcase}_#{event.to_s}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,7 +17,7 @@
|
|||
= link_to_project project
|
||||
%ul.well-list.issues_table
|
||||
- group[1].each do |issue|
|
||||
= render(partial: 'issues/show', locals: {issue: issue})
|
||||
= render issue
|
||||
%hr
|
||||
= paginate @issues, theme: "gitlab"
|
||||
- else
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
= link_to_project project
|
||||
%ul.well-list.issues_table
|
||||
- group[1].each do |issue|
|
||||
= render(partial: 'issues/show', locals: {issue: issue})
|
||||
= render issue
|
||||
%hr
|
||||
= paginate @issues, theme: "gitlab"
|
||||
- else
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- @issues.each do |issue|
|
||||
= render(partial: 'issues/show', locals: {issue: issue})
|
||||
= render @issues
|
||||
|
||||
- if @issues.present?
|
||||
%li.bottom
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
= @issue.created_at.stamp("Aug 21, 2011")
|
||||
|
||||
%span.pull-right
|
||||
- if can?(current_user, :admin_project, @project) || @issue.author == current_user
|
||||
- if can?(current_user, :modify_issue, @issue)
|
||||
- if @issue.closed?
|
||||
= link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue"
|
||||
- else
|
||||
= link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue"
|
||||
- if can?(current_user, :admin_project, @project) || @issue.author == current_user
|
||||
|
||||
= link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
|
||||
%i.icon-edit
|
||||
Edit
|
||||
|
@ -55,5 +55,11 @@
|
|||
= preserve do
|
||||
= markdown @issue.description
|
||||
|
||||
- content_for :note_actions do
|
||||
- if can?(current_user, :modify_issue, @issue)
|
||||
- if @issue.closed?
|
||||
= link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue"
|
||||
- else
|
||||
= link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue"
|
||||
|
||||
.voting_notes#notes= render "notes/notes_with_form"
|
||||
|
|
|
@ -29,10 +29,10 @@
|
|||
$(function(){
|
||||
merge_request = new MergeRequest({
|
||||
url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}",
|
||||
check_enable: #{@merge_request.merge_status == MergeRequest::UNCHECKED ? "true" : "false"},
|
||||
check_enable: #{@merge_request.unchecked? ? "true" : "false"},
|
||||
url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}",
|
||||
ci_enable: #{@project.gitlab_ci? ? "true" : "false"},
|
||||
current_status: "#{@merge_request.human_merge_status}",
|
||||
current_status: "#{@merge_request.merge_status_name}",
|
||||
action: "#{controller.action_name}"
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
%br/
|
||||
.content
|
||||
%table
|
||||
- each_diff_line(diff, note.diff_file_index) do |line, type, line_code, line_new, line_old|
|
||||
- each_diff_line_near(diff, note.diff_file_index, note.line_code) do |line, type, line_code, line_new, line_old|
|
||||
%tr.line_holder{ id: line_code }
|
||||
- if type == "match"
|
||||
%td.old_line= "..."
|
||||
|
@ -22,4 +22,3 @@
|
|||
|
||||
- if line_code == note.line_code
|
||||
= render "notes/diff_notes_with_reply", notes: discussion_notes
|
||||
- break # cut off diff after notes
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
.note-form-actions
|
||||
.buttons
|
||||
= f.submit 'Add Comment', class: "btn comment-btn grouped js-comment-button"
|
||||
= yield(:note_actions)
|
||||
|
||||
%a.btn.grouped.js-close-discussion-note-form Cancel
|
||||
|
||||
.note-form-option
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
|
||||
|
||||
%fieldset
|
||||
%fieldset.update-token
|
||||
%legend
|
||||
Private token
|
||||
%span.cred.pull-right
|
||||
|
@ -29,7 +29,7 @@
|
|||
%span You don`t have one yet. Click generate to fix it.
|
||||
= f.submit 'Generate', class: "btn success btn-build-token"
|
||||
|
||||
%fieldset
|
||||
%fieldset.update-password
|
||||
%legend Password
|
||||
= form_for @user, url: update_password_profile_path, method: :put do |f|
|
||||
.padded
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
= @snippet.title
|
||||
%small= @snippet.file_name
|
||||
- if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user
|
||||
= link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small pull-right"
|
||||
= link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small pull-right", title: 'Edit Snippet'
|
||||
|
||||
%br
|
||||
%div= render 'blob'
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
= link_to_project @project
|
||||
%ul.well-list.issues_table
|
||||
- group[1].each do |issue|
|
||||
= render(partial: 'issues/show', locals: {issue: issue})
|
||||
= render issue
|
||||
%hr
|
||||
= paginate @issues, theme: "gitlab"
|
||||
- else
|
||||
|
|
|
@ -26,5 +26,5 @@
|
|||
- elsif user.blocked
|
||||
%span.btn.disabled.blocked Blocked
|
||||
- elsif allow_admin
|
||||
= link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove" do
|
||||
= link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove", title: "Remove from team" do
|
||||
%i.icon-minus.icon-white
|
||||
|
|
|
@ -42,6 +42,6 @@ class PostReceive
|
|||
return false
|
||||
end
|
||||
|
||||
project.trigger_post_receive(oldrev, newrev, ref, user)
|
||||
GitPushService.new.execute(project, user, oldrev, newrev, ref)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue