From e166a8022a3f239938a1449a0a8ce3485f309766 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Fri, 29 Apr 2016 16:25:03 +0200 Subject: [PATCH] Backend for a gitignores dropdown --- app/helpers/blob_helper.rb | 4 ++ app/models/repository.rb | 20 +++++-- app/views/projects/blob/_editor.html.haml | 3 + app/views/projects/empty.html.haml | 10 +++- lib/api/api.rb | 1 + lib/api/entities.rb | 8 +++ lib/api/gitignores.rb | 29 ++++++++++ lib/gitlab/gitignore.rb | 68 +++++++++++++++++++++++ lib/tasks/gitlab/update_gitignore.rake | 26 +++++++++ spec/lib/gitlab/gitignore_spec.rb | 38 +++++++++++++ spec/requests/api/gitignores_spec.rb | 29 ++++++++++ 11 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 lib/api/gitignores.rb create mode 100644 lib/gitlab/gitignore.rb create mode 100644 lib/tasks/gitlab/update_gitignore.rake create mode 100644 spec/lib/gitlab/gitignore_spec.rb create mode 100644 spec/requests/api/gitignores_spec.rb diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 93241b3afb7..fb1b7649465 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -184,4 +184,8 @@ module BlobHelper Other: licenses.reject(&:featured).map { |license| [license.name, license.key] } } end + + def gitignores_for_select + @gitignores_for_select ||= Gitlab::Gitignore.all + end end diff --git a/app/models/repository.rb b/app/models/repository.rb index 47a7223c723..f26278cc3af 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -472,9 +472,7 @@ class Repository def changelog cache.fetch(:changelog) do - tree(:head).blobs.find do |file| - file.name =~ /\A(changelog|history|changes|news)/i - end + file_on_head(/\A(changelog|history|changes|news)/i) end end @@ -482,9 +480,7 @@ class Repository return nil unless head_exists? cache.fetch(:license_blob) do - tree(:head).blobs.find do |file| - file.name =~ /\A(licen[sc]e|copying)(\..+|\z)/i - end + file_on_head(/\A(licen[sc]e|copying)(\..+|\z)/i) end end @@ -496,6 +492,14 @@ class Repository end end + def gitignore + return nil if !exists? || empty? + + cache.fetch(:gitignore) do + file_on_head(/\A\.gitignore\z/) + end + end + def gitlab_ci_yml return nil unless head_exists? @@ -989,4 +993,8 @@ class Repository def head_exists? exists? && !empty? && !rugged.head_unborn? end + + def file_on_head(regex) + tree(:head).blobs.find { |file| file.name =~ regex } + end end diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index fefa652a3da..eac5014b7c9 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -16,6 +16,9 @@ .license-selector.js-license-selector.hide = select_tag :license_type, grouped_options_for_select(licenses_for_select, @project.repository.license_key), include_blank: true, class: 'select2 license-select', data: {placeholder: 'Choose a license template', project: @project.name, fullname: @project.namespace.human_name} + .gitignore-selector.js-gitignore-selector.hide + = select_tag :gitignore_template, options_for_select(Gitlab::Gitignore.all), include_blank: true, class: 'select2 gitignore-select', data: {placeholder: 'Choose a .gitignore template'} + .encoding-selector = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2' diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 1a2e59752fe..636beb73ec2 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -15,10 +15,14 @@ If you already have files you can push them using command line instructions below. %p Otherwise you can start with adding a - = link_to "README", new_readme_path, class: 'underlined-link' + = succeed ',' do + = link_to "README", new_readme_path, class: 'underlined-link' + a + = succeed ',' do + = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link' or a - = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link' - file to this project. + = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link' + to this project. - if can?(current_user, :push_code, @project) %div{ class: container_class } diff --git a/lib/api/api.rb b/lib/api/api.rb index 360fb41a721..6cd909f6115 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -58,5 +58,6 @@ module API mount ::API::Runners mount ::API::Licenses mount ::API::Subscriptions + mount ::API::Gitignores end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 8298e3ad34c..31491cf31dd 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -457,5 +457,13 @@ module API expose(:limitations) { |license| license.meta['limitations'] } expose :content end + + class GitignoresList < Grape::Entity + expose :name + end + + class Gitignore < Grape::Entity + expose :name, :content + end end end diff --git a/lib/api/gitignores.rb b/lib/api/gitignores.rb new file mode 100644 index 00000000000..1af9ba6b316 --- /dev/null +++ b/lib/api/gitignores.rb @@ -0,0 +1,29 @@ +module API + class Gitignores < Grape::API + + # Get the list of the available gitignore templates + # + # Example Request: + # GET /gitignores + get 'gitignores' do + present Gitlab::Gitignore.all, with: Entities::GitignoresList + end + + # Get the text for a specific gitignore + # + # Parameters: + # key (required) - The key of a license + # + # Example Request: + # GET /gitignores/elixir + # + get 'gitignores/:key' do + required_attributes! [:key] + + gitignore = Gitlab::Gitignore.find(params[:key]) + not_found!('.gitignore') unless gitignore + + present gitignore, with: Entities::Gitignore + end + end +end diff --git a/lib/gitlab/gitignore.rb b/lib/gitlab/gitignore.rb new file mode 100644 index 00000000000..a2de2831e38 --- /dev/null +++ b/lib/gitlab/gitignore.rb @@ -0,0 +1,68 @@ +module Gitlab + class Gitignore + FILTER_REGEX = /\.gitignore\z/.freeze + + attr_accessor :name, :directory + + def initialize(name, directory) + @name = name + @directory = directory + end + + def content + File.read(path) + end + + class << self + def all + languages_frameworks + global + end + + def find(key) + file_name = "#{key}.gitignore" + + directory = select_directory(file_name) + directory ? new(key, directory) : nil + end + + def global + files_for_folder(global_dir).map { |f| new(f, global_dir) } + end + + def languages_frameworks + files_for_folder(gitignore_dir).map { |f| new(f, gitignore_dir) } + end + end + + private + + def path + File.expand_path("#{name}.gitignore", directory) + end + + class << self + def select_directory(file_name) + [self.gitignore_dir, self.global_dir].find { |dir| File.exist?(File.expand_path(file_name, dir)) } + end + + def global_dir + File.expand_path('Global', gitignore_dir) + end + + def gitignore_dir + File.expand_path('vendor/gitignore', Rails.root) + end + + def files_for_folder(dir) + gitignores = [] + Dir.entries(dir).each do |e| + next unless e.end_with?('.gitignore') + + gitignores << e.gsub(FILTER_REGEX, '') + end + + gitignores + end + end + end +end diff --git a/lib/tasks/gitlab/update_gitignore.rake b/lib/tasks/gitlab/update_gitignore.rake new file mode 100644 index 00000000000..61cbfd6737d --- /dev/null +++ b/lib/tasks/gitlab/update_gitignore.rake @@ -0,0 +1,26 @@ +namespace :gitlab do + desc "GitLab | Update gitignore" + task :update_gitignore do + dir = File.expand_path('vendor', Rails.root) + FileUtils.cd(dir) + + dir = File.expand_path('gitignore', dir) + clone_gitignores(dir) + remove_unneeded_files(dir) + + puts "Done".green + end + + def clone_gitignores(dir) + FileUtils.rm_rf(dir) if Dir.exist?(dir) + system('git clone --depth=1 --branch=master https://github.com/github/gitignore.git') + end + + def remove_unneeded_files(dir) + [File.expand_path('Global', dir), dir].each do |path| + Dir.entries(path).reject { |e| e =~ /(\.{1,2}|Global|\.gitignore)\z/ }.each do |file| + FileUtils.rm_rf File.expand_path(file, path) + end + end + end +end diff --git a/spec/lib/gitlab/gitignore_spec.rb b/spec/lib/gitlab/gitignore_spec.rb new file mode 100644 index 00000000000..5dab821a8ec --- /dev/null +++ b/spec/lib/gitlab/gitignore_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe Gitlab::Gitignore do + subject { Gitlab::Gitignore } + describe '.all' do + it 'strips the gitignore suffix' do + expect(subject.all.first.name).not_to end_with('.gitignore') + end + + it 'combines the globals and rest' do + all = subject.all.map(&:name) + + expect(all).to include('Vim') + expect(all).to include('Ruby') + end + end + + describe '.find' do + it 'returns nil if the file does not exist' do + expect(subject.find('mepmep-yadida')).to be nil + end + + it 'returns the Gitignore object of a valid file' do + ruby = subject.find('Ruby') + + expect(ruby).to be_a Gitlab::Gitignore + end + end + + describe '#content' do + it 'loads the full file' do + gitignore = subject.new('Ruby', File.expand_path('vendor/gitignore', Rails.root)) + + expect(gitignore.name).to eq 'Ruby' + expect(gitignore.content).to start_with('*.gem') + end + end +end diff --git a/spec/requests/api/gitignores_spec.rb b/spec/requests/api/gitignores_spec.rb new file mode 100644 index 00000000000..d0e576e637c --- /dev/null +++ b/spec/requests/api/gitignores_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe API::Gitignores, api: true do + include ApiHelpers + + describe 'Entity Gitignore' do + before { get api('/gitignores/Ruby') } + + it { expect(json_response['name']).to eq('Ruby') } + it { expect(json_response['content']).to include('*.gem') } + end + + describe 'Entity GitignoresList' do + before { get api('/gitignores') } + + it { expect(json_response.first['name']).to be_truthy } + it { expect(json_response.first['content']).to be_falsey } + end + + describe 'GET /gitignores' do + it 'returns a list of available license templates' do + get api('/gitignores') + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to be > 15 + end + end +end -- GitLab