Merge branch 'repo-remove'
Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> Conflicts: spec/features/projects_spec.rb
This commit is contained in:
commit
e414463d9e
7 changed files with 106 additions and 41 deletions
|
@ -42,6 +42,7 @@ v 7.12.0 (unreleased)
|
|||
- Add an option to automatically sign-in with an Omniauth provider
|
||||
- Better performance for web editor (switched from satellites to rugged)
|
||||
- GitLab CI service sends .gitlab-ci.yaml in each push call
|
||||
- When remove project - move repository and schedule it removal
|
||||
|
||||
v 7.11.4
|
||||
- Fix missing bullets when creating lists
|
||||
|
|
|
@ -97,18 +97,15 @@ class ProjectsController < ApplicationController
|
|||
return access_denied! unless can?(current_user, :remove_project, @project)
|
||||
|
||||
::Projects::DestroyService.new(@project, current_user, {}).execute
|
||||
flash[:alert] = 'Project deleted.'
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
flash[:alert] = 'Project deleted.'
|
||||
|
||||
if request.referer.include?('/admin')
|
||||
redirect_to admin_namespaces_projects_path
|
||||
else
|
||||
redirect_to dashboard_path
|
||||
end
|
||||
end
|
||||
if request.referer.include?('/admin')
|
||||
redirect_to admin_namespaces_projects_path
|
||||
else
|
||||
redirect_to dashboard_path
|
||||
end
|
||||
rescue Projects::DestroyService::DestroyError => ex
|
||||
redirect_to edit_project_path(@project), alert: ex.message
|
||||
end
|
||||
|
||||
def autocomplete_sources
|
||||
|
|
|
@ -1,28 +1,67 @@
|
|||
module Projects
|
||||
class DestroyService < BaseService
|
||||
include Gitlab::ShellAdapter
|
||||
|
||||
class DestroyError < StandardError; end
|
||||
|
||||
DELETED_FLAG = '+deleted'
|
||||
|
||||
def execute
|
||||
return false unless can?(current_user, :remove_project, project)
|
||||
|
||||
project.team.truncate
|
||||
project.repository.expire_cache unless project.empty_repo?
|
||||
|
||||
if project.destroy
|
||||
GitlabShellWorker.perform_async(
|
||||
:remove_repository,
|
||||
project.path_with_namespace
|
||||
)
|
||||
repo_path = project.path_with_namespace
|
||||
wiki_path = repo_path + '.wiki'
|
||||
|
||||
GitlabShellWorker.perform_async(
|
||||
:remove_repository,
|
||||
project.path_with_namespace + ".wiki"
|
||||
)
|
||||
Project.transaction do
|
||||
project.destroy!
|
||||
|
||||
project.satellite.destroy
|
||||
unless remove_repository(repo_path)
|
||||
raise_error('Failed to remove project repository. Please try again or contact administrator')
|
||||
end
|
||||
|
||||
log_info("Project \"#{project.name}\" was removed")
|
||||
system_hook_service.execute_hooks_for(project, :destroy)
|
||||
true
|
||||
unless remove_repository(wiki_path)
|
||||
raise_error('Failed to remove wiki repository. Please try again or contact administrator')
|
||||
end
|
||||
end
|
||||
|
||||
project.satellite.destroy
|
||||
log_info("Project \"#{project.name}\" was removed")
|
||||
system_hook_service.execute_hooks_for(project, :destroy)
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def remove_repository(path)
|
||||
unless gitlab_shell.exists?(path + '.git')
|
||||
return true
|
||||
end
|
||||
|
||||
new_path = removal_path(path)
|
||||
|
||||
if gitlab_shell.mv_repository(path, new_path)
|
||||
log_info("Repository \"#{path}\" moved to \"#{new_path}\"")
|
||||
GitlabShellWorker.perform_in(5.minutes, :remove_repository, new_path)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def raise_error(message)
|
||||
raise DestroyError.new(message)
|
||||
end
|
||||
|
||||
# Build a path for removing repositories
|
||||
# We use `+` because its not allowed by GitLab so user can not create
|
||||
# project with name cookies+119+deleted and capture someone stalled repository
|
||||
#
|
||||
# gitlab/cookies.git -> gitlab/cookies+119+deleted.git
|
||||
#
|
||||
def removal_path(path)
|
||||
"#{path}+#{project.id}#{DELETED_FLAG}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -244,6 +244,16 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
# Check if such directory exists in repositories.
|
||||
#
|
||||
# Usage:
|
||||
# exists?('gitlab')
|
||||
# exists?('gitlab/cookies.git')
|
||||
#
|
||||
def exists?(dir_name)
|
||||
File.exists?(full_path(dir_name))
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def gitlab_shell_path
|
||||
|
@ -264,10 +274,6 @@ module Gitlab
|
|||
File.join(repos_path, dir_name)
|
||||
end
|
||||
|
||||
def exists?(dir_name)
|
||||
File.exists?(full_path(dir_name))
|
||||
end
|
||||
|
||||
def gitlab_shell_projects_path
|
||||
File.join(gitlab_shell_path, 'bin', 'gitlab-projects')
|
||||
end
|
||||
|
|
|
@ -47,13 +47,6 @@ feature 'Project' do
|
|||
it 'should remove project' do
|
||||
expect { remove_project }.to change {Project.count}.by(-1)
|
||||
end
|
||||
|
||||
it 'should delete the project from disk' do
|
||||
expect(GitlabShellWorker).to receive(:perform_async).
|
||||
with(:remove_repository, /#{project.path_with_namespace}/).twice
|
||||
|
||||
remove_project
|
||||
end
|
||||
end
|
||||
|
||||
def remove_project
|
||||
|
|
|
@ -57,14 +57,14 @@ describe API::API, api: true do
|
|||
expect(json_response.first['name']).to eq(project.name)
|
||||
expect(json_response.first['owner']['username']).to eq(user.username)
|
||||
end
|
||||
|
||||
|
||||
it 'should include the project labels as the tag_list' do
|
||||
get api('/projects', user)
|
||||
response.status.should == 200
|
||||
json_response.should be_an Array
|
||||
json_response.first.keys.should include('tag_list')
|
||||
end
|
||||
|
||||
|
||||
context 'and using search' do
|
||||
it 'should return searched project' do
|
||||
get api('/projects', user), { search: project.name }
|
||||
|
@ -792,11 +792,6 @@ describe API::API, api: true do
|
|||
describe 'DELETE /projects/:id' do
|
||||
context 'when authenticated as user' do
|
||||
it 'should remove project' do
|
||||
expect(GitlabShellWorker).to(
|
||||
receive(:perform_async).with(:remove_repository,
|
||||
/#{project.path_with_namespace}/)
|
||||
).twice
|
||||
|
||||
delete api("/projects/#{project.id}", user)
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
|
34
spec/services/projects/destroy_service_spec.rb
Normal file
34
spec/services/projects/destroy_service_spec.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Projects::DestroyService do
|
||||
let!(:user) { create(:user) }
|
||||
let!(:project) { create(:project, namespace: user.namespace) }
|
||||
let!(:path) { project.repository.path_to_repo }
|
||||
let!(:remove_path) { path.sub(/\.git\Z/, "+#{project.id}+deleted.git") }
|
||||
|
||||
context 'Sidekiq inline' do
|
||||
before do
|
||||
# Run sidekiq immediatly to check that renamed repository will be removed
|
||||
Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
|
||||
end
|
||||
|
||||
it { Project.all.should_not include(project) }
|
||||
it { Dir.exists?(path).should be_falsey }
|
||||
it { Dir.exists?(remove_path).should be_falsey }
|
||||
end
|
||||
|
||||
context 'Sidekiq fake' do
|
||||
before do
|
||||
# Dont run sidekiq to check if renamed repository exists
|
||||
Sidekiq::Testing.fake! { destroy_project(project, user, {}) }
|
||||
end
|
||||
|
||||
it { Project.all.should_not include(project) }
|
||||
it { Dir.exists?(path).should be_falsey }
|
||||
it { Dir.exists?(remove_path).should be_truthy }
|
||||
end
|
||||
|
||||
def destroy_project(project, user, params)
|
||||
Projects::DestroyService.new(project, user, params).execute
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue