diff --git a/CHANGELOG b/CHANGELOG index 201990669a21d727c6a02f3518583b717adc8dfd..55a1a22e6b7d1bc931448b91440e1bf040b3e84d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ v 6.7.0 - Add GFM autocompletion for MergeRequests (Robert Speicher) - Add webhook when a new tag is pushed (Jeroen van Baarsen) - Add button for toggling inline comments in diff view + - Add retry feature for repository import v 6.6.2 - Fix 500 error on branch/tag create or remove via UI diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index f1c0336e6ea8b7bf37f0c0a76766851780a12ce9..8d24052f724dfab265f1c721cce56150c249a93e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -5,7 +5,7 @@ class ProjectsController < ApplicationController # Authorize before_filter :authorize_read_project!, except: [:index, :new, :create] - before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive] + before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import] before_filter :require_non_empty_project, only: [:blob, :tree, :graph] layout 'navless', only: [:new, :create, :fork] @@ -21,16 +21,9 @@ class ProjectsController < ApplicationController def create @project = ::Projects::CreateService.new(current_user, params[:project]).execute + flash[:notice] = 'Project was successfully created.' if @project.saved? respond_to do |format| - flash[:notice] = 'Project was successfully created.' if @project.saved? - format.html do - if @project.saved? - redirect_to @project - else - render "new" - end - end format.js end end @@ -55,6 +48,11 @@ class ProjectsController < ApplicationController end def show + if @project.import_in_progress? + redirect_to import_project_path(@project) + return + end + return authenticate_user! unless @project.public? || current_user limit = (params[:limit] || 20).to_i @@ -67,9 +65,7 @@ class ProjectsController < ApplicationController if @project.empty_repo? render "projects/empty", layout: user_layout else - if current_user - @last_push = current_user.recent_push(@project.id) - end + @last_push = current_user.recent_push(@project.id) if current_user render :show, layout: user_layout end end @@ -77,6 +73,28 @@ class ProjectsController < ApplicationController 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 + + @project.import_url = params[:project][:import_url] + + if @project.save + @project.reload + @project.import_retry + end + + redirect_to import_project_path(@project) + end + def destroy return access_denied! unless can?(current_user, :remove_project, project) diff --git a/app/models/project.rb b/app/models/project.rb index 47dc8a1fdb0c8e77ae408c68d98071095b4457ea..392c38cc5d9ee05b6ffcbcf758bceb884d46c02a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -28,7 +28,6 @@ class Project < ActiveRecord::Base include Gitlab::VisibilityLevel extend Enumerize - default_value_for :imported, false default_value_for :archived, false ActsAsTaggableOn.strict_case_match = true @@ -59,13 +58,10 @@ class Project < ActiveRecord::Base has_one :gemnasium_service, dependent: :destroy has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link - # Merge Requests for target project should be removed with it has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id" - # Merge requests from source project should be kept when source project was removed has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest - has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy has_many :services, dependent: :destroy has_many :events, dependent: :destroy @@ -74,10 +70,8 @@ class Project < ActiveRecord::Base has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet" has_many :hooks, dependent: :destroy, class_name: "ProjectHook" has_many :protected_branches, dependent: :destroy - has_many :users_projects, dependent: :destroy has_many :users, through: :users_projects - has_many :deploy_keys_projects, dependent: :destroy has_many :deploy_keys, through: :deploy_keys_projects @@ -97,15 +91,12 @@ class Project < ActiveRecord::Base validates :issues_enabled, :wall_enabled, :merge_requests_enabled, :wiki_enabled, inclusion: { in: [true, false] } validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true - validates :namespace, presence: true validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :path, scope: :namespace_id - validates :import_url, format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" }, if: :import? - validate :check_limit, on: :create # Scopes @@ -118,14 +109,36 @@ class Project < ActiveRecord::Base scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") } scope :personal, ->(user) { where(namespace_id: user.namespace_id) } scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } - scope :public_only, -> { where(visibility_level: Project::PUBLIC) } scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } - scope :non_archived, -> { where(archived: false) } enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab + state_machine :import_status, initial: :none do + event :import_start do + transition :none => :started + end + + event :import_finish do + transition :started => :finished + end + + event :import_fail do + transition :started => :failed + end + + event :import_retry do + transition :failed => :started + end + + state :started + state :finished + state :failed + + after_transition any => :started, :do => :add_import_job + end + class << self def public_and_internal_levels [Project::PUBLIC, Project::INTERNAL] @@ -202,12 +215,28 @@ class Project < ActiveRecord::Base id && persisted? end + def add_import_job + RepositoryImportWorker.perform_in(2.seconds, id) + end + def import? import_url.present? end def imported? - imported + import_finished? + end + + def import_in_progress? + import? && import_status == 'started' + end + + def import_failed? + import_status == 'failed' + end + + def import_finished? + import_status == 'finished' end def check_limit diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb index 4e3deec01bf38943c33f45fd12e59734ed8381c7..ad41ddad58fac7d45f1ab4689eb25713b3fc9ef9 100644 --- a/app/observers/project_observer.rb +++ b/app/observers/project_observer.rb @@ -1,30 +1,6 @@ class ProjectObserver < BaseObserver def after_create(project) - project.update_column(:last_activity_at, project.created_at) - - return true if project.forked? - - if project.import? - RepositoryImportWorker.perform_in(5.seconds, project.id) - else - GitlabShellWorker.perform_async( - :add_repository, - project.path_with_namespace - ) - - log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") - end - - if project.wiki_enabled? - begin - # force the creation of a wiki, - GollumWiki.new(project, project.owner).wiki - rescue GollumWiki::CouldNotCreateWikiError => ex - # Prevent project observer crash - # if failed to create wiki - nil - end - end + log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") end def after_update(project) diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index ba131d8ffbe73f5b95f39bde1c38d884890d624f..4d3d518a509533ff21d7045c6feca7c78170ba70 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -58,6 +58,29 @@ module Projects user: current_user ) 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 + + if @project.wiki_enabled? + begin + # force the creation of a wiki, + GollumWiki.new(@project, @project.owner).wiki + rescue GollumWiki::CouldNotCreateWikiError => ex + # Prevent project observer crash + # if failed to create wiki + nil + end + end end @project diff --git a/app/views/projects/create.js.haml b/app/views/projects/create.js.haml index a444b8b59a622075249fc7b61b84b8807559cc85..89710d3a09afc202021444fbbb01da4a5295b3bb 100644 --- a/app/views/projects/create.js.haml +++ b/app/views/projects/create.js.haml @@ -1,6 +1,10 @@ - if @project.saved? - :plain - location.href = "#{project_path(@project)}"; + - 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'))}"); diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 489b9b0e951b5c8715e507e62db1533582f46ef7..97dc73bce1465bff8cbb99c700532d366babf63f 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,46 +1,34 @@ = render "home_panel" -- if @project.import? && !@project.imported - .save-project-loader - %center - %h2 - %i.icon-spinner.icon-spin - Importing repository. - %p.monospace git clone --bare #{@project.import_url} - %p Please wait while we import the repository for you. Refresh at will. - :javascript - new ProjectImport(); +%div.git-empty + %fieldset + %legend Git global setup: + %pre.dark + :preserve + git config --global user.name "#{git_user_name}" + git config --global user.email "#{git_user_email}" -- else - %div.git-empty - %fieldset - %legend Git global setup: - %pre.dark - :preserve - git config --global user.name "#{git_user_name}" - git config --global user.email "#{git_user_email}" + %fieldset + %legend Create Repository + %pre.dark + :preserve + mkdir #{@project.path} + cd #{@project.path} + git init + touch README + git add README + git commit -m 'first commit' + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git push -u origin master - %fieldset - %legend Create Repository - %pre.dark - :preserve - mkdir #{@project.path} - cd #{@project.path} - git init - touch README - git add README - git commit -m 'first commit' - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git push -u origin master + %fieldset + %legend Existing Git Repo? + %pre.dark + :preserve + cd existing_git_repo + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git push -u origin master - %fieldset - %legend Existing Git Repo? - %pre.dark - :preserve - cd existing_git_repo - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git push -u origin master - - - 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" +- 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" diff --git a/app/views/projects/import.html.haml b/app/views/projects/import.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..d11372be61b25f57b05ead2d3e53c3b34cc523b8 --- /dev/null +++ b/app/views/projects/import.html.haml @@ -0,0 +1,30 @@ +- if @project.import_in_progress? + .save-project-loader + %center + %h2 + %i.icon-spinner.icon-spin + Import in progress. + %p.monospace git clone --bare #{@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 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 2 minutes. For big repositories, use a clone/push combination. + .form-actions + = f.submit 'Retry import', class: "btn btn-create", tabindex: 4 diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 9cc28c7f535dc42b8509dbb56e4fcda9541dfa61..5d5637c15884a040961b4779216dd535ef9aa037 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -23,6 +23,7 @@ .col-sm-2 .col-sm-10 = link_to "#", class: 'js-toggle-button' do + %i.icon-edit %span Customize repository name? .js-toggle-content.hide .form-group @@ -46,8 +47,10 @@ %span Import existing repo .col-sm-10 = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' - .light - URL must be cloneable + .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 2 minutes. For big repositories, use a clone/push combination. %hr .form-group diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 95b80bca7c0d3839815561d17b83d2141de86da2..e66df8c4db17ade0ce2d8f09882847d59353ee8e 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -11,11 +11,11 @@ class RepositoryImportWorker project.import_url) if result - project.imported = true + project.import_finish project.save project.satellite.create unless project.satellite.exists? else - project.imported = false + project.import_fail end end end diff --git a/config/routes.rb b/config/routes.rb index fdca1e62661a87506fe63ff84efa6467757838a3..628d1f631bc5bf0d6e28500320b8f66031670e93 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -179,6 +179,8 @@ Gitlab::Application.routes.draw do post :archive post :unarchive get :autocomplete_sources + get :import + put :retry_import end scope module: :projects do diff --git a/db/migrate/20140312145357_add_import_status_to_project.rb b/db/migrate/20140312145357_add_import_status_to_project.rb new file mode 100644 index 0000000000000000000000000000000000000000..ef972e8342a0e4f0105dc8aabc5f98d4f1e344b2 --- /dev/null +++ b/db/migrate/20140312145357_add_import_status_to_project.rb @@ -0,0 +1,5 @@ +class AddImportStatusToProject < ActiveRecord::Migration + def change + add_column :projects, :import_status, :string + end +end diff --git a/db/migrate/20140313092127_migrate_already_imported_projects.rb b/db/migrate/20140313092127_migrate_already_imported_projects.rb new file mode 100644 index 0000000000000000000000000000000000000000..f4392c0f05e8db595c9cd3524623d109b2c3dd52 --- /dev/null +++ b/db/migrate/20140313092127_migrate_already_imported_projects.rb @@ -0,0 +1,12 @@ +class MigrateAlreadyImportedProjects < ActiveRecord::Migration + def up + Project.where(imported: true).update_all(import_status: "finished") + Project.where(imported: false).update_all(import_status: "none") + remove_column :projects, :imported + end + + def down + add_column :projects, :imported, :boolean, default: false + Project.where(import_status: 'finished').update_all(imported: true) + end +end diff --git a/db/schema.rb b/db/schema.rb index e07fd3deaca0d98c6fad25839d76d89a9542ef14..d8a9d1863fc7c63744dc1253fa02f44c0df0b46d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140305193308) do +ActiveRecord::Schema.define(version: 20140313092127) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -214,10 +214,10 @@ ActiveRecord::Schema.define(version: 20140305193308) do t.string "issues_tracker_id" t.boolean "snippets_enabled", default: true, null: false t.datetime "last_activity_at" - t.boolean "imported", default: false, null: false t.string "import_url" t.integer "visibility_level", default: 0, null: false t.boolean "archived", default: false, null: false + t.string "import_status" end add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree