diff options
-rw-r--r-- | app/assets/images/project_templates/rails.png | bin | 0 -> 4221 bytes | |||
-rw-r--r-- | app/controllers/import/gitlab_projects_controller.rb | 10 | ||||
-rw-r--r-- | app/controllers/projects_controller.rb | 12 | ||||
-rw-r--r-- | app/models/project.rb | 1 | ||||
-rw-r--r-- | app/services/projects/create_from_template_service.rb | 14 | ||||
-rw-r--r-- | app/services/projects/gitlab_projects_importer_service.rb | 33 | ||||
-rw-r--r-- | app/views/projects/_project_templates.html.haml | 8 | ||||
-rw-r--r-- | app/views/projects/new.html.haml | 59 | ||||
-rw-r--r-- | lib/gitlab/import_export.rb | 4 | ||||
-rw-r--r-- | lib/gitlab/project_template.rb | 39 | ||||
-rw-r--r-- | spec/features/projects_spec.rb | 21 | ||||
-rw-r--r-- | spec/lib/gitlab/project_template_spec.rb | 48 | ||||
-rw-r--r-- | spec/services/projects/create_from_template_service_spec.rb | 26 | ||||
-rw-r--r-- | vendor/project_templates/rails.tar.gz | bin | 0 -> 899958 bytes |
14 files changed, 238 insertions, 37 deletions
diff --git a/app/assets/images/project_templates/rails.png b/app/assets/images/project_templates/rails.png Binary files differnew file mode 100644 index 00000000000..dbee6bf6227 --- /dev/null +++ b/app/assets/images/project_templates/rails.png diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb index 36d246d185b..6463d2dfd5b 100644 --- a/app/controllers/import/gitlab_projects_controller.rb +++ b/app/controllers/import/gitlab_projects_controller.rb @@ -12,15 +12,7 @@ class Import::GitlabProjectsController < Import::BaseController return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." }) end - import_upload_path = Gitlab::ImportExport.import_upload_path(filename: project_params[:file].original_filename) - - FileUtils.mkdir_p(File.dirname(import_upload_path)) - FileUtils.copy_entry(project_params[:file].path, import_upload_path) - - @project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id], - current_user, - import_upload_path, - project_params[:path]).execute + @project = ::Projects::GitlabProjectsImporterService.new(current_user, project_params).execute if @project.saved? redirect_to( diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index c769693255c..275474d02f6 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -27,7 +27,12 @@ class ProjectsController < Projects::ApplicationController end def create - @project = ::Projects::CreateService.new(current_user, project_params).execute + @project = + if project_from_template? + ::Projects::CreateFromTemplateService.new(current_user, project_params).execute + else + ::Projects::CreateService.new(current_user, project_params).execute + end if @project.saved? cookies[:issue_board_welcome_hidden] = { path: project_path(@project), value: nil, expires: Time.at(0) } @@ -324,6 +329,7 @@ class ProjectsController < Projects::ApplicationController :runners_token, :tag_list, :visibility_level, + :template_title, project_feature_attributes: %i[ builds_access_level @@ -345,6 +351,10 @@ class ProjectsController < Projects::ApplicationController false end + def project_from_template? + project_params[:template_title]&.present? + end + def project_view_files? if current_user current_user.project_view == 'files' diff --git a/app/models/project.rb b/app/models/project.rb index d827bfaa806..e7c404bf817 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -71,6 +71,7 @@ class Project < ActiveRecord::Base attr_accessor :new_default_branch attr_accessor :old_path_with_namespace + attr_accessor :template_title attr_writer :pipeline_status alias_attribute :title, :name diff --git a/app/services/projects/create_from_template_service.rb b/app/services/projects/create_from_template_service.rb new file mode 100644 index 00000000000..3fc5c4ad157 --- /dev/null +++ b/app/services/projects/create_from_template_service.rb @@ -0,0 +1,14 @@ +module Projects + class CreateFromTemplateService < BaseService + def initialize(user, params) + @current_user, @params = user, params.dup + end + + def execute + params[:file] = Gitlab::ProjectTemplate.find(params[:template_title]).file + + @params[:from_template] = true + GitlabProjectsImporterService.new(@current_user, @params).execute + end + end +end diff --git a/app/services/projects/gitlab_projects_importer_service.rb b/app/services/projects/gitlab_projects_importer_service.rb new file mode 100644 index 00000000000..4cb98c54de5 --- /dev/null +++ b/app/services/projects/gitlab_projects_importer_service.rb @@ -0,0 +1,33 @@ +# This service is an adapter used to for the GitLab Import feature, and +# creating a project from a template. +# The latter will under the hood just import an archive supplied by GitLab. +module Projects + class GitlabProjectsImporterService + attr_reader :current_user, :params + + def initialize(user, params) + @current_user, @params = user, params.dup + end + + def execute + FileUtils.mkdir_p(File.dirname(import_upload_path)) + FileUtils.copy_entry(file.path, import_upload_path) + + Gitlab::ImportExport::ProjectCreator.new(params[:namespace_id], + current_user, + import_upload_path, + params[:path]).execute + end + + private + + def import_upload_path + @import_upload_path ||= Gitlab::ImportExport + .import_upload_path(filename: "#{params[:namespace_id]}-#{params[:path]}") + end + + def file + params[:file] + end + end +end diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml new file mode 100644 index 00000000000..bef64ca7433 --- /dev/null +++ b/app/views/projects/_project_templates.html.haml @@ -0,0 +1,8 @@ +.col-sm-12.template-buttons + - Gitlab::ProjectTemplate.all.each do |template| + -# The title should be the value posted to the controller, a pretty name to print would be + -# template.name + = template.title + = image_tag(template.logo_path) + + = f.text_field :template_title, placeholder: "rails", class: "form-control", tabindex: 2, autofocus: true, required: true diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index a2d7a21d5f6..c01645e2ec9 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -17,36 +17,17 @@ Create or Import your project from popular Git services .col-lg-9 = form_for @project, html: { class: 'new_project' } do |f| - .row - .form-group.col-xs-12.col-sm-6 - = f.label :namespace_id, class: 'label-light' do - %span - Project path - .form-group - .input-group - - if current_user.can_select_namespace? - .input-group-addon - = root_url - = f.select :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), {}, { class: 'select2 js-select-namespace', tabindex: 1} - - - else - .input-group-addon.static-namespace - #{root_url}#{current_user.username}/ - = f.hidden_field :namespace_id, value: current_user.namespace_id - .form-group.col-xs-12.col-sm-6.project-path - = f.label :path, class: 'label-light' do - %span - Project name - = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true - - if current_user.can_create_group? - .help-block - Want to house several dependent projects under the same namespace? - = link_to "Create a group", new_group_path + .project-template.js-toggle-container + .form_group.clearfix + = f.label :template_project, class: 'label-light' do + Start from template + .col-sm-12.import-buttons + = render 'project_templates', f: f - if import_sources_enabled? .project-import.js-toggle-container .form-group.clearfix - = f.label :visibility_level, class: 'label-light' do + = f.label :visibility_level, class: 'label-light' do #the label here seems wrong Import project from .col-sm-12.import-buttons %div @@ -90,6 +71,32 @@ .js-toggle-content.hide = render "shared/import_form", f: f + .row + .form-group.col-xs-12.col-sm-6 + = f.label :namespace_id, class: 'label-light' do + %span + Project path + .form-group + .input-group + - if current_user.can_select_namespace? + .input-group-addon + = root_url + = f.select :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), {}, { class: 'select2 js-select-namespace', tabindex: 1} + + - else + .input-group-addon.static-namespace + #{root_url}#{current_user.username}/ + = f.hidden_field :namespace_id, value: current_user.namespace_id + .form-group.col-xs-12.col-sm-6.project-path + = f.label :path, class: 'label-light' do + %span + Project name + = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true + - if current_user.can_create_group? + .help-block + Want to house several dependent projects under the same namespace? + = link_to "Create a group", new_group_path + .form-group = f.label :description, class: 'label-light' do Project description diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb index 3470a09eaf0..9f23d29218b 100644 --- a/lib/gitlab/import_export.rb +++ b/lib/gitlab/import_export.rb @@ -15,7 +15,9 @@ module Gitlab end def import_upload_path(filename:) - File.join(storage_path, 'uploads', filename) + milliseconds = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond) + + File.join(storage_path, 'uploads', "#{millisecond}-#{filename}") end def project_filename diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb new file mode 100644 index 00000000000..72fcda154c5 --- /dev/null +++ b/lib/gitlab/project_template.rb @@ -0,0 +1,39 @@ +module Gitlab + class ProjectTemplate + attr_reader :title, :name + + def initialize(name, title) + @name, @title = name, title + end + + def logo_path + "project_templates/#{name}.png" + end + + def file + template_archive.open + end + + def template_archive + Rails.root.join("vendor/project_templates/#{name}.tar.gz") + end + + def ==(other) + name == other.name && title == other.title + end + + TemplatesTable = [ + ProjectTemplate.new('rails', 'Ruby on Rails') + ].freeze + + class << self + def all + TemplatesTable + end + + def find(name) + all.find { |template| template.name == name.to_s } + end + end + end +end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 10c7e5934e4..ab5042490ae 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -1,6 +1,27 @@ require 'spec_helper' feature 'Project', feature: true do + describe 'creating from template' do + let(:user) { create(:user) } + let(:template) { Gitlab::ProjectTemplate.find(:rails) } + + before do + sign_in user + visit new_project_path + end + + it "allows creation from the #{template.name} template" do + fill_in("project_template_title", with: template.title) + fill_in("project_path", with: template.name) + + page.within '#content-body' do + click_button "Create project" + end + + expect(page).to have_content 'Import' + end + end + describe 'description' do let(:project) { create(:project, :repository) } let(:path) { project_path(project) } diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb new file mode 100644 index 00000000000..5dc6059b49c --- /dev/null +++ b/spec/lib/gitlab/project_template_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe Gitlab::ProjectTemplate do + describe '.all' do + it 'returns a all templates' do + expected = [ + described_class.new('rails', 'Ruby on Rails') + ] + + expect(described_class.all).to be_an(Array) + expect(described_class.all).to eq(expected) + end + end + + describe '.find' do + subject { described_class.find(query) } + + context 'when there is a match' do + let(:query) { :rails } + + it { is_expected.to be_a(described_class) } + end + + context 'when there is no match' do + let(:query) { 'no-match' } + + it { is_expected.to be(nil) } + end + end + + describe 'instance methods' do + subject { described_class.new('phoenix', 'Phoenix Framework') } + + it { is_expected.to respond_to(:logo_path, :file, :template_archive) } + end + + describe 'validate all templates' do + described_class.all.each do |template| + it "#{template.name} has a valid archive" do + archive = template.template_archive + logo = Rails.root.join("app/assets/images/#{template.logo_path}") + + expect(File.exist?(archive)).to be(true) + expect(File.exist?(logo)).to be(true) + end + end + end +end diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb new file mode 100644 index 00000000000..81e88c0862d --- /dev/null +++ b/spec/services/projects/create_from_template_service_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe Projects::CreateFromTemplateService do + let(:user) { create(:user) } + let(:project_params) do + { + path: user.to_param, + template_title: 'rails' + } + end + + subject { described_class.new(user, project_params) } + + it 'calls the importer service' do + expect_any_instance_of(Projects::GitlabProjectsImporterService).to receive(:execute) + + subject.execute + end + + it 'returns the project thats created' do + project = subject.execute + + expect(project).to be_saved + expect(project.import_status).to eq('scheduled') + end +end diff --git a/vendor/project_templates/rails.tar.gz b/vendor/project_templates/rails.tar.gz Binary files differnew file mode 100644 index 00000000000..b54cae3143a --- /dev/null +++ b/vendor/project_templates/rails.tar.gz |