diff options
-rw-r--r-- | app/models/repository.rb | 28 | ||||
-rw-r--r-- | app/models/tree.rb | 4 | ||||
-rw-r--r-- | lib/gitlab/file_detector.rb | 63 | ||||
-rw-r--r-- | spec/lib/gitlab/file_detector_spec.rb | 59 | ||||
-rw-r--r-- | spec/models/repository_spec.rb | 94 |
5 files changed, 232 insertions, 16 deletions
diff --git a/app/models/repository.rb b/app/models/repository.rb index 31be06be50c..06c9eb3bd70 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -321,7 +321,7 @@ class Repository def cache_keys %i(size commit_count readme version contribution_guide changelog - license_blob license_key gitignore koding_yml) + license_blob license_key gitignore koding_yml gitlab_ci_yml) end # Keys for data on branch/tag operations. @@ -556,23 +556,19 @@ class Repository def version cache.fetch(:version) do - tree(:head).blobs.find do |file| - file.name.casecmp('version').zero? - end + file_on_head(:version) end end def contribution_guide cache.fetch(:contribution_guide) do - tree(:head).blobs.find do |file| - file.contributing? - end + file_on_head(:contributing) end end def changelog cache.fetch(:changelog) do - file_on_head(/\A(changelog|history|changes|news)/i) + file_on_head(:changelog) end end @@ -580,7 +576,7 @@ class Repository return nil unless head_exists? cache.fetch(:license_blob) do - file_on_head(/\A(licen[sc]e|copying)(\..+|\z)/i) + file_on_head(:license) end end @@ -596,7 +592,7 @@ class Repository return nil if !exists? || empty? cache.fetch(:gitignore) do - file_on_head(/\A\.gitignore\z/) + file_on_head(:gitignore) end end @@ -604,15 +600,15 @@ class Repository return nil unless head_exists? cache.fetch(:koding_yml) do - file_on_head(/\A\.koding\.yml\z/) + file_on_head(:koding) end end def gitlab_ci_yml return nil unless head_exists? - @gitlab_ci_yml ||= tree(:head).blobs.find do |file| - file.name == '.gitlab-ci.yml' + @gitlab_ci_yml ||= cache.fetch(:gitlab_ci_yml) do + file_on_head(:gitlab_ci) end rescue Rugged::ReferenceError # For unknow reason spinach scenario "Scenario: I change project path" @@ -1160,8 +1156,10 @@ class Repository exists? && !empty? && !rugged.head_unborn? end - def file_on_head(regex) - tree(:head).blobs.find { |file| file.name =~ regex } + def file_on_head(type) + tree(:head).blobs.find do |file| + Gitlab::FileDetector.type_of(file.name) == type + end end def tags_sorted_by_committed_date diff --git a/app/models/tree.rb b/app/models/tree.rb index 2d1d68dbd81..fe148b0ec65 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -18,7 +18,9 @@ class Tree def readme return @readme if defined?(@readme) - available_readmes = blobs.select(&:readme?) + available_readmes = blobs.select do |blob| + Gitlab::FileDetector.type_of(blob.name) == :readme + end previewable_readmes = available_readmes.select do |blob| previewable?(blob.name) diff --git a/lib/gitlab/file_detector.rb b/lib/gitlab/file_detector.rb new file mode 100644 index 00000000000..1d93a67dc56 --- /dev/null +++ b/lib/gitlab/file_detector.rb @@ -0,0 +1,63 @@ +require 'set' + +module Gitlab + # Module that can be used to detect if a path points to a special file such as + # a README or a CONTRIBUTING file. + module FileDetector + PATTERNS = { + readme: /\Areadme/i, + changelog: /\A(changelog|history|changes|news)/i, + license: /\A(licen[sc]e|copying)(\..+|\z)/i, + contributing: /\Acontributing/i, + version: 'version', + gitignore: '.gitignore', + koding: '.koding.yml', + gitlab_ci: '.gitlab-ci.yml', + avatar: /\Alogo\.(png|jpg|gif)\z/ + } + + # Returns an Array of file types based on the given paths. + # + # This method can be used to check if a list of file paths (e.g. of changed + # files) involve any special files such as a README or a LICENSE file. + # + # Example: + # + # types_in_paths(%w{README.md foo/bar.txt}) # => [:readme] + def self.types_in_paths(paths) + types = Set.new + + paths.each do |path| + type = type_of(path) + + types << type if type + end + + types.to_a + end + + # Returns the type of a file path, or nil if none could be detected. + # + # Returned types are Symbols such as `:readme`, `:version`, etc. + # + # Example: + # + # type_of('README.md') # => :readme + # type_of('VERSION') # => :version + def self.type_of(path) + name = File.basename(path) + + PATTERNS.each do |type, search| + did_match = if search.is_a?(Regexp) + name =~ search + else + name.casecmp(search) == 0 + end + + return type if did_match + end + + nil + end + end +end diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb new file mode 100644 index 00000000000..e5ba13bbaf8 --- /dev/null +++ b/spec/lib/gitlab/file_detector_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe Gitlab::FileDetector do + describe '.types_in_paths' do + it 'returns the file types for the given paths' do + expect(described_class.types_in_paths(%w(README.md CHANGELOG VERSION VERSION))). + to eq(%i{readme changelog version}) + end + + it 'does not include unrecognized file paths' do + expect(described_class.types_in_paths(%w(README.md foo.txt))). + to eq(%i{readme}) + end + end + + describe '.type_of' do + it 'returns the type of a README file' do + expect(described_class.type_of('README.md')).to eq(:readme) + end + + it 'returns the type of a changelog file' do + %w(CHANGELOG HISTORY CHANGES NEWS).each do |file| + expect(described_class.type_of(file)).to eq(:changelog) + end + end + + it 'returns the type of a license file' do + %w(LICENSE LICENCE COPYING).each do |file| + expect(described_class.type_of(file)).to eq(:license) + end + end + + it 'returns the type of a version file' do + expect(described_class.type_of('VERSION')).to eq(:version) + end + + it 'returns the type of a .gitignore file' do + expect(described_class.type_of('.gitignore')).to eq(:gitignore) + end + + it 'returns the type of a Koding config file' do + expect(described_class.type_of('.koding.yml')).to eq(:koding) + end + + it 'returns the type of a GitLab CI config file' do + expect(described_class.type_of('.gitlab-ci.yml')).to eq(:gitlab_ci) + end + + it 'returns the type of an avatar' do + %w(logo.gif logo.png logo.jpg).each do |file| + expect(described_class.type_of(file)).to eq(:avatar) + end + end + + it 'returns nil for an unknown file' do + expect(described_class.type_of('foo.txt')).to be_nil + end + end +end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 72ac41f3472..5e8a7bb08a1 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1578,4 +1578,98 @@ describe Repository, models: true do end.to raise_error(Repository::CommitError) end end + + describe '#contribution_guide', caching: true do + it 'returns and caches the output' do + expect(repository).to receive(:file_on_head). + with(:contributing). + and_return(Gitlab::Git::Tree.new(path: 'CONTRIBUTING.md')). + once + + 2.times do + expect(repository.contribution_guide). + to be_an_instance_of(Gitlab::Git::Tree) + end + end + end + + describe '#changelog', caching: true do + it 'returns and caches the output' do + expect(repository).to receive(:file_on_head). + with(:changelog). + and_return(Gitlab::Git::Tree.new(path: 'CHANGELOG')). + once + + 2.times do + expect(repository.changelog).to be_an_instance_of(Gitlab::Git::Tree) + end + end + end + + describe '#license_blob', caching: true do + it 'returns and caches the output' do + expect(repository).to receive(:file_on_head). + with(:license). + and_return(Gitlab::Git::Tree.new(path: 'LICENSE')). + once + + 2.times do + expect(repository.license_blob).to be_an_instance_of(Gitlab::Git::Tree) + end + end + end + + describe '#license_key', caching: true do + it 'returns and caches the output' do + license = double(key: 'mit') + + expect(Licensee).to receive(:license). + with(repository.path). + and_return(license). + once + + 2.times do + expect(repository.license_key).to eq('mit') + end + end + end + + describe '#gitignore', caching: true do + it 'returns and caches the output' do + expect(repository).to receive(:file_on_head). + with(:gitignore). + and_return(Gitlab::Git::Tree.new(path: '.gitignore')). + once + + 2.times do + expect(repository.gitignore).to be_an_instance_of(Gitlab::Git::Tree) + end + end + end + + describe '#koding_yml', caching: true do + it 'returns and caches the output' do + expect(repository).to receive(:file_on_head). + with(:koding). + and_return(Gitlab::Git::Tree.new(path: '.koding.yml')). + once + + 2.times do + expect(repository.koding_yml).to be_an_instance_of(Gitlab::Git::Tree) + end + end + end + + describe '#gitlab_ci_yml', caching: true do + it 'returns and caches the output' do + expect(repository).to receive(:file_on_head). + with(:gitlab_ci). + and_return(Gitlab::Git::Tree.new(path: '.gitlab-ci.yml')). + once + + 2.times do + expect(repository.gitlab_ci_yml).to be_an_instance_of(Gitlab::Git::Tree) + end + end + end end |