Skip to content

Commit 0d7986a

Browse files
committed
Merge branch 'import-timeout' of https://dev.gitlab.org/dzaporozhets/gitlabhq into dzaporozhets/gitlabhq-import-timeout
Signed-off-by: Dmitriy Zaporozhets <[email protected]> Conflicts: CHANGELOG db/schema.rb
2 parents 58e4e6b + 9064fba commit 0d7986a

14 files changed

+189
-98
lines changed

CHANGELOG

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ v 6.7.0
1717
- Add GFM autocompletion for MergeRequests (Robert Speicher)
1818
- Add webhook when a new tag is pushed (Jeroen van Baarsen)
1919
- Add button for toggling inline comments in diff view
20+
- Add retry feature for repository import
2021

2122
v 6.6.2
2223
- Fix 500 error on branch/tag create or remove via UI

app/controllers/projects_controller.rb

+30-12
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class ProjectsController < ApplicationController
55

66
# Authorize
77
before_filter :authorize_read_project!, except: [:index, :new, :create]
8-
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
8+
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import]
99
before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
1010

1111
layout 'navless', only: [:new, :create, :fork]
@@ -21,16 +21,9 @@ def edit
2121

2222
def create
2323
@project = ::Projects::CreateService.new(current_user, params[:project]).execute
24+
flash[:notice] = 'Project was successfully created.' if @project.saved?
2425

2526
respond_to do |format|
26-
flash[:notice] = 'Project was successfully created.' if @project.saved?
27-
format.html do
28-
if @project.saved?
29-
redirect_to @project
30-
else
31-
render "new"
32-
end
33-
end
3427
format.js
3528
end
3629
end
@@ -55,6 +48,11 @@ def transfer
5548
end
5649

5750
def show
51+
if @project.import_in_progress?
52+
redirect_to import_project_path(@project)
53+
return
54+
end
55+
5856
return authenticate_user! unless @project.public? || current_user
5957

6058
limit = (params[:limit] || 20).to_i
@@ -67,16 +65,36 @@ def show
6765
if @project.empty_repo?
6866
render "projects/empty", layout: user_layout
6967
else
70-
if current_user
71-
@last_push = current_user.recent_push(@project.id)
72-
end
68+
@last_push = current_user.recent_push(@project.id) if current_user
7369
render :show, layout: user_layout
7470
end
7571
end
7672
format.json { pager_json("events/_events", @events.count) }
7773
end
7874
end
7975

76+
def import
77+
if project.import_finished?
78+
redirect_to @project
79+
return
80+
end
81+
end
82+
83+
def retry_import
84+
unless @project.import_failed?
85+
redirect_to import_project_path(@project)
86+
end
87+
88+
@project.import_url = params[:project][:import_url]
89+
90+
if @project.save
91+
@project.reload
92+
@project.import_retry
93+
end
94+
95+
redirect_to import_project_path(@project)
96+
end
97+
8098
def destroy
8199
return access_denied! unless can?(current_user, :remove_project, project)
82100

app/models/project.rb

+41-12
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ class Project < ActiveRecord::Base
2828
include Gitlab::VisibilityLevel
2929
extend Enumerize
3030

31-
default_value_for :imported, false
3231
default_value_for :archived, false
3332

3433
ActsAsTaggableOn.strict_case_match = true
@@ -59,13 +58,10 @@ class Project < ActiveRecord::Base
5958
has_one :gemnasium_service, dependent: :destroy
6059
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
6160
has_one :forked_from_project, through: :forked_project_link
62-
6361
# Merge Requests for target project should be removed with it
6462
has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id"
65-
6663
# Merge requests from source project should be kept when source project was removed
6764
has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest
68-
6965
has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy
7066
has_many :services, dependent: :destroy
7167
has_many :events, dependent: :destroy
@@ -74,10 +70,8 @@ class Project < ActiveRecord::Base
7470
has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet"
7571
has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
7672
has_many :protected_branches, dependent: :destroy
77-
7873
has_many :users_projects, dependent: :destroy
7974
has_many :users, through: :users_projects
80-
8175
has_many :deploy_keys_projects, dependent: :destroy
8276
has_many :deploy_keys, through: :deploy_keys_projects
8377

@@ -97,15 +91,12 @@ class Project < ActiveRecord::Base
9791
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
9892
:wiki_enabled, inclusion: { in: [true, false] }
9993
validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true
100-
10194
validates :namespace, presence: true
10295
validates_uniqueness_of :name, scope: :namespace_id
10396
validates_uniqueness_of :path, scope: :namespace_id
104-
10597
validates :import_url,
10698
format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" },
10799
if: :import?
108-
109100
validate :check_limit, on: :create
110101

111102
# Scopes
@@ -118,14 +109,36 @@ class Project < ActiveRecord::Base
118109
scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") }
119110
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
120111
scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
121-
122112
scope :public_only, -> { where(visibility_level: Project::PUBLIC) }
123113
scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) }
124-
125114
scope :non_archived, -> { where(archived: false) }
126115

127116
enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab
128117

118+
state_machine :import_status, initial: :none do
119+
event :import_start do
120+
transition :none => :started
121+
end
122+
123+
event :import_finish do
124+
transition :started => :finished
125+
end
126+
127+
event :import_fail do
128+
transition :started => :failed
129+
end
130+
131+
event :import_retry do
132+
transition :failed => :started
133+
end
134+
135+
state :started
136+
state :finished
137+
state :failed
138+
139+
after_transition any => :started, :do => :add_import_job
140+
end
141+
129142
class << self
130143
def public_and_internal_levels
131144
[Project::PUBLIC, Project::INTERNAL]
@@ -202,12 +215,28 @@ def saved?
202215
id && persisted?
203216
end
204217

218+
def add_import_job
219+
RepositoryImportWorker.perform_in(2.seconds, id)
220+
end
221+
205222
def import?
206223
import_url.present?
207224
end
208225

209226
def imported?
210-
imported
227+
import_finished?
228+
end
229+
230+
def import_in_progress?
231+
import? && import_status == 'started'
232+
end
233+
234+
def import_failed?
235+
import_status == 'failed'
236+
end
237+
238+
def import_finished?
239+
import_status == 'finished'
211240
end
212241

213242
def check_limit

app/observers/project_observer.rb

+1-25
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,6 @@
11
class ProjectObserver < BaseObserver
22
def after_create(project)
3-
project.update_column(:last_activity_at, project.created_at)
4-
5-
return true if project.forked?
6-
7-
if project.import?
8-
RepositoryImportWorker.perform_in(5.seconds, project.id)
9-
else
10-
GitlabShellWorker.perform_async(
11-
:add_repository,
12-
project.path_with_namespace
13-
)
14-
15-
log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
16-
end
17-
18-
if project.wiki_enabled?
19-
begin
20-
# force the creation of a wiki,
21-
GollumWiki.new(project, project.owner).wiki
22-
rescue GollumWiki::CouldNotCreateWikiError => ex
23-
# Prevent project observer crash
24-
# if failed to create wiki
25-
nil
26-
end
27-
end
3+
log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
284
end
295

306
def after_update(project)

app/services/projects/create_service.rb

+23
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,29 @@ def execute
5858
user: current_user
5959
)
6060
end
61+
62+
@project.update_column(:last_activity_at, @project.created_at)
63+
64+
if @project.import?
65+
@project.import_start
66+
else
67+
GitlabShellWorker.perform_async(
68+
:add_repository,
69+
@project.path_with_namespace
70+
)
71+
72+
end
73+
74+
if @project.wiki_enabled?
75+
begin
76+
# force the creation of a wiki,
77+
GollumWiki.new(@project, @project.owner).wiki
78+
rescue GollumWiki::CouldNotCreateWikiError => ex
79+
# Prevent project observer crash
80+
# if failed to create wiki
81+
nil
82+
end
83+
end
6184
end
6285

6386
@project

app/views/projects/create.js.haml

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
- if @project.saved?
2-
:plain
3-
location.href = "#{project_path(@project)}";
2+
- if @project.import?
3+
:plain
4+
location.href = "#{import_project_path(@project)}";
5+
- else
6+
:plain
7+
location.href = "#{project_path(@project)}";
48
- else
59
:plain
610
$(".project-edit-errors").html("#{escape_javascript(render('errors'))}");

app/views/projects/empty.html.haml

+29-41
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,34 @@
11
= render "home_panel"
22

3-
- if @project.import? && [email protected]
4-
.save-project-loader
5-
%center
6-
%h2
7-
%i.icon-spinner.icon-spin
8-
Importing repository.
9-
%p.monospace git clone --bare #{@project.import_url}
10-
%p Please wait while we import the repository for you. Refresh at will.
11-
:javascript
12-
new ProjectImport();
3+
%div.git-empty
4+
%fieldset
5+
%legend Git global setup:
6+
%pre.dark
7+
:preserve
8+
git config --global user.name "#{git_user_name}"
9+
git config --global user.email "#{git_user_email}"
1310

14-
- else
15-
%div.git-empty
16-
%fieldset
17-
%legend Git global setup:
18-
%pre.dark
19-
:preserve
20-
git config --global user.name "#{git_user_name}"
21-
git config --global user.email "#{git_user_email}"
11+
%fieldset
12+
%legend Create Repository
13+
%pre.dark
14+
:preserve
15+
mkdir #{@project.path}
16+
cd #{@project.path}
17+
git init
18+
touch README
19+
git add README
20+
git commit -m 'first commit'
21+
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
22+
git push -u origin master
2223

23-
%fieldset
24-
%legend Create Repository
25-
%pre.dark
26-
:preserve
27-
mkdir #{@project.path}
28-
cd #{@project.path}
29-
git init
30-
touch README
31-
git add README
32-
git commit -m 'first commit'
33-
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
34-
git push -u origin master
24+
%fieldset
25+
%legend Existing Git Repo?
26+
%pre.dark
27+
:preserve
28+
cd existing_git_repo
29+
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
30+
git push -u origin master
3531

36-
%fieldset
37-
%legend Existing Git Repo?
38-
%pre.dark
39-
:preserve
40-
cd existing_git_repo
41-
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
42-
git push -u origin master
43-
44-
- if can? current_user, :remove_project, @project
45-
.prepend-top-20
46-
= link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
32+
- if can? current_user, :remove_project, @project
33+
.prepend-top-20
34+
= link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"

app/views/projects/import.html.haml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
- if @project.import_in_progress?
2+
.save-project-loader
3+
%center
4+
%h2
5+
%i.icon-spinner.icon-spin
6+
Import in progress.
7+
%p.monospace git clone --bare #{@project.import_url}
8+
%p Please wait while we import the repository for you. Refresh at will.
9+
:javascript
10+
new ProjectImport();
11+
12+
- elsif @project.import_failed?
13+
.save-project-loader
14+
%center
15+
%h2
16+
Import failed. Retry?
17+
%hr
18+
- if can?(current_user, :admin_project, @project)
19+
= form_for @project, url: retry_import_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f|
20+
.form-group.import-url-data
21+
= f.label :import_url, class: 'control-label' do
22+
%span Import existing repo
23+
.col-sm-10
24+
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
25+
.bs-callout.bs-callout-info
26+
This url must be publicly accessible or you can add a username and password like this: https://username:[email protected]/company/project.git.
27+
%br
28+
The import will time out after 2 minutes. For big repositories, use a clone/push combination.
29+
.form-actions
30+
= f.submit 'Retry import', class: "btn btn-create", tabindex: 4

0 commit comments

Comments
 (0)