Commit f6562be0 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'sync-project-create' into 'master'

Create project & repository at the same time

* block project creation if repository cant be created
* show proper error message about failed to create repository
* dont use ajax for project creation any more - its fast enough
* if project repository does not exist - propose to create one or import
* move import functionality to separate resource

Fixes #1768

See merge request !1278
parents 27cd35de 5c1496a4
...@@ -7,8 +7,8 @@ v 7.6.0 ...@@ -7,8 +7,8 @@ v 7.6.0
- Add optional Sidekiq MemoryKiller middleware (enabled via SIDEKIQ_MAX_RSS env variable) - Add optional Sidekiq MemoryKiller middleware (enabled via SIDEKIQ_MAX_RSS env variable)
- -
- -
- - Create project with repository in synchrony
- - Added ability to create empty repo or import existing one if project does not have repository
- -
- -
- -
......
class Projects::ImportsController < Projects::ApplicationController
# Authorize
before_filter :authorize_admin_project!
before_filter :require_no_repo
before_filter :redirect_if_progress, except: :show
def new
end
def create
@project.import_url = params[:project][:import_url]
if @project.save
@project.reload
if @project.import_failed?
@project.import_retry
else
@project.import_start
end
end
redirect_to project_import_path(@project)
end
def show
unless @project.import_in_progress?
if @project.import_finished?
redirect_to(@project) and return
else
redirect_to new_project_import_path(@project) and return
end
end
end
private
def require_no_repo
if @project.repository_exists?
redirect_to(@project) and return
end
end
def redirect_if_progress
if @project.import_in_progress?
redirect_to project_import_path(@project) and return
end
end
end
class Projects::RepositoriesController < Projects::ApplicationController class Projects::RepositoriesController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_download_code! before_filter :authorize_download_code!
before_filter :require_non_empty_project before_filter :require_non_empty_project, except: :create
before_filter :authorize_admin_project!, only: :create
def create
@project.create_repository
redirect_to @project
end
def archive def archive
unless can?(current_user, :download_code, @project) unless can?(current_user, :download_code, @project)
......
...@@ -4,7 +4,7 @@ class ProjectsController < ApplicationController ...@@ -4,7 +4,7 @@ class ProjectsController < ApplicationController
before_filter :repository, except: [:new, :create] before_filter :repository, except: [:new, :create]
# Authorize # Authorize
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import] before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
layout 'navless', only: [:new, :create, :fork] layout 'navless', only: [:new, :create, :fork]
before_filter :set_title, only: [:new, :create] before_filter :set_title, only: [:new, :create]
...@@ -19,10 +19,11 @@ class ProjectsController < ApplicationController ...@@ -19,10 +19,11 @@ class ProjectsController < ApplicationController
def create def create
@project = ::Projects::CreateService.new(current_user, project_params).execute @project = ::Projects::CreateService.new(current_user, project_params).execute
flash[:notice] = 'Project was successfully created.' if @project.saved?
respond_to do |format| if @project.saved?
format.js redirect_to project_path(@project), notice: 'Project was successfully created.'
else
render 'new'
end end
end end
...@@ -47,7 +48,7 @@ class ProjectsController < ApplicationController ...@@ -47,7 +48,7 @@ class ProjectsController < ApplicationController
def show def show
if @project.import_in_progress? if @project.import_in_progress?
redirect_to import_project_path(@project) redirect_to project_import_path(@project)
return return
end end
...@@ -60,37 +61,20 @@ class ProjectsController < ApplicationController ...@@ -60,37 +61,20 @@ class ProjectsController < ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
if @project.repository_exists?
if @project.empty_repo? if @project.empty_repo?
render "projects/empty", layout: user_layout render "projects/empty", layout: user_layout
else else
@last_push = current_user.recent_push(@project.id) if current_user @last_push = current_user.recent_push(@project.id) if current_user
render :show, layout: user_layout render :show, layout: user_layout
end end
else
render "projects/no_repo", layout: user_layout
end end
format.json { pager_json("events/_events", @events.count) }
end
end
def import
if @project.import_finished?
redirect_to @project
return
end
end
def retry_import
unless @project.import_failed?
redirect_to import_project_path(@project)
end end
@project.import_url = project_params[:import_url] format.json { pager_json("events/_events", @events.count) }
if @project.save
@project.reload
@project.import_retry
end end
redirect_to import_project_path(@project)
end end
def destroy def destroy
......
...@@ -136,7 +136,7 @@ class Project < ActiveRecord::Base ...@@ -136,7 +136,7 @@ class Project < ActiveRecord::Base
state_machine :import_status, initial: :none do state_machine :import_status, initial: :none do
event :import_start do event :import_start do
transition :none => :started transition [:none, :finished] => :started
end end
event :import_finish do event :import_finish do
...@@ -586,4 +586,25 @@ class Project < ActiveRecord::Base ...@@ -586,4 +586,25 @@ class Project < ActiveRecord::Base
def origin_merge_requests def origin_merge_requests
merge_requests.where(source_project_id: self.id) merge_requests.where(source_project_id: self.id)
end end
def create_repository
if gitlab_shell.add_repository(path_with_namespace)
true
else
errors.add(:base, "Failed to create repository")
false
end
end
def repository_exists?
!!repository.exists?
end
def create_wiki
ProjectWiki.new(self, self.owner).wiki
true
rescue ProjectWiki::CouldNotCreateWikiError => ex
errors.add(:base, "Failed create wiki")
false
end
end end
...@@ -37,35 +37,22 @@ module Projects ...@@ -37,35 +37,22 @@ module Projects
@project.creator = current_user @project.creator = current_user
if @project.save Project.transaction do
log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"") @project.save
system_hook_service.execute_hooks_for(@project, :create)
unless @project.group unless @project.import?
@project.team << [current_user, :master] unless @project.create_repository
raise 'Failed to create repository'
end
end end
@project.update_column(:last_activity_at, @project.created_at)
if @project.import?
@project.import_start
else
GitlabShellWorker.perform_async(
:add_repository,
@project.path_with_namespace
)
end end
if @project.persisted?
if @project.wiki_enabled? if @project.wiki_enabled?
begin @project.create_wiki
# force the creation of a wiki,
ProjectWiki.new(@project, @project.owner).wiki
rescue ProjectWiki::CouldNotCreateWikiError => ex
# Prevent project observer crash
# if failed to create wiki
nil
end
end end
after_create_actions
end end
@project @project
...@@ -84,5 +71,20 @@ module Projects ...@@ -84,5 +71,20 @@ module Projects
namespace = Namespace.find_by(id: namespace_id) namespace = Namespace.find_by(id: namespace_id)
current_user.can?(:create_projects, namespace) current_user.can?(:create_projects, namespace)
end end
def after_create_actions
log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
system_hook_service.execute_hooks_for(@project, :create)
unless @project.group
@project.team << [current_user, :master]
end
@project.update_column(:last_activity_at, @project.created_at)
if @project.import?
@project.import_start
end
end
end end
end end
- if @project.saved?
- if @project.import?
:plain
location.href = "#{import_project_path(@project)}";
- else
:plain
location.href = "#{project_path(@project)}";
- else
:plain
$(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
$('.project-submit').enable();
$('.save-project-loader').hide();
$('.project-edit-container').show();
- if @project.import_in_progress?
.save-project-loader
.center
%h2
%i.fa.fa-spinner.fa-spin
Import in progress.
%p.monospace git clone --bare #{hidden_pass_url(@project.import_url)}
%p Please wait while we import the repository for you. Refresh at will.
:javascript
new ProjectImport();
- elsif @project.import_failed?
.save-project-loader
.center
%h2
Import failed. Retry?
%hr
- if can?(current_user, :admin_project, @project)
= form_for @project, url: retry_import_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f|
.form-group.import-url-data
= f.label :import_url, class: 'control-label' do
%span Import existing git repo
.col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
.bs-callout.bs-callout-info
This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br
The import will time out after 4 minutes. For big repositories, use a clone/push combination.
For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
.form-actions
= f.submit 'Retry import', class: "btn btn-create", tabindex: 4
%h3.page-title
- if @project.import_failed?
Import failed. Retry?
- else
Import repository
%hr
= form_for @project, url: project_import_path(@project), method: :post, html: { class: 'form-horizontal' } do |f|
.form-group.import-url-data
= f.label :import_url, class: 'control-label' do
%span Import existing git repo
.col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
.bs-callout.bs-callout-info
This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br
The import will time out after 4 minutes. For big repositories, use a clone/push combination.
For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
.form-actions
= f.submit 'Start import', class: "btn btn-create", tabindex: 4
.save-project-loader
.center
%h2
%i.fa.fa-spinner.fa-spin
Import in progress.
%p.monospace git clone --bare #{hidden_pass_url(@project.import_url)}
%p Please wait while we import the repository for you. Refresh at will.
:javascript
new ProjectImport();
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= render 'projects/errors' = render 'projects/errors'
.project-edit-content .project-edit-content
= form_for @project, remote: true, html: { class: 'new_project form-horizontal' } do |f| = form_for @project, html: { class: 'new_project form-horizontal' } do |f|
.form-group.project-name-holder .form-group.project-name-holder
= f.label :name, class: 'control-label' do = f.label :name, class: 'control-label' do
%strong Project name %strong Project name
......
%h2
%i.fa.fa-warning
No repository
%p.slead
The repository for this project does not exist.
%br
This means you can not push code until you create an empty repository or import existing one.
%hr
.no-repo-actions
= link_to project_repository_path(@project), method: :post, class: 'btn btn-primary' do
Create empty bare repository
%strong.prepend-left-10.append-right-10 or
= link_to new_project_import_path(@project), class: 'btn' do
Import repository
- if can? current_user, :remove_project, @project
.prepend-top-20
= link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
...@@ -186,8 +186,6 @@ Gitlab::Application.routes.draw do ...@@ -186,8 +186,6 @@ Gitlab::Application.routes.draw do
post :upload_image post :upload_image
post :toggle_star post :toggle_star
get :autocomplete_sources get :autocomplete_sources
get :import
put :retry_import
end end
scope module: :projects do scope module: :projects do
...@@ -232,8 +230,9 @@ Gitlab::Application.routes.draw do ...@@ -232,8 +230,9 @@ Gitlab::Application.routes.draw do
end end
resource :fork, only: [:new, :create] resource :fork, only: [:new, :create]
resource :import, only: [:new, :create, :show]
resource :repository, only: [:show] do resource :repository, only: [:show, :create] do
member do member do
get "archive", constraints: { format: Gitlab::Regex.archive_formats_regex } get "archive", constraints: { format: Gitlab::Regex.archive_formats_regex }
end end
......
...@@ -131,7 +131,7 @@ module SharedProject ...@@ -131,7 +131,7 @@ module SharedProject
end end
step 'public empty project "Empty Public Project"' do step 'public empty project "Empty Public Project"' do
create :empty_project, :public, name: "Empty Public Project" create :project_empty_repo, :public, name: "Empty Public Project"
end end
step 'project "Community" has comments' do step 'project "Community" has comments' do
......
...@@ -27,6 +27,10 @@ ...@@ -27,6 +27,10 @@
# #
FactoryGirl.define do FactoryGirl.define do
# Project without repository
#
# Project does not have bare repository.
# Use this factory if you dont need repository in tests
factory :empty_project, class: 'Project' do factory :empty_project, class: 'Project' do
sequence(:name) { |n| "project#{n}" } sequence(:name) { |n| "project#{n}" }
path { name.downcase.gsub(/\s/, '_') } path { name.downcase.gsub(/\s/, '_') }
...@@ -47,6 +51,20 @@ FactoryGirl.define do ...@@ -47,6 +51,20 @@ FactoryGirl.define do
end end
end end
# Project with empty repository
#
# This is a case when you just created a project
# but not pushed any code there yet
factory :project_empty_repo, parent: :empty_project do
after :create do |project|
project.create_repository
end
end
# Project with test repository
#
# Test repository source can be found at
# https://gitlab.com/gitlab-org/gitlab-test
factory :project, parent: :empty_project do factory :project, parent: :empty_project do
path { 'gitlabhq' } path { 'gitlabhq' }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment