summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/images/project_templates/rails.pngbin0 -> 4221 bytes
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb10
-rw-r--r--app/controllers/projects_controller.rb12
-rw-r--r--app/models/project.rb1
-rw-r--r--app/services/projects/create_from_template_service.rb14
-rw-r--r--app/services/projects/gitlab_projects_importer_service.rb33
-rw-r--r--app/views/projects/_project_templates.html.haml8
-rw-r--r--app/views/projects/new.html.haml59
-rw-r--r--lib/gitlab/import_export.rb4
-rw-r--r--lib/gitlab/project_template.rb39
-rw-r--r--spec/features/projects_spec.rb21
-rw-r--r--spec/lib/gitlab/project_template_spec.rb48
-rw-r--r--spec/services/projects/create_from_template_service_spec.rb26
-rw-r--r--vendor/project_templates/rails.tar.gzbin0 -> 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
new file mode 100644
index 00000000000..dbee6bf6227
--- /dev/null
+++ b/app/assets/images/project_templates/rails.png
Binary files differ
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
new file mode 100644
index 00000000000..b54cae3143a
--- /dev/null
+++ b/vendor/project_templates/rails.tar.gz
Binary files differ