summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/models/repository.rb28
-rw-r--r--app/models/tree.rb4
-rw-r--r--lib/gitlab/file_detector.rb63
-rw-r--r--spec/lib/gitlab/file_detector_spec.rb59
-rw-r--r--spec/models/repository_spec.rb94
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