Commit df5548e1 authored by Yorick Peterse's avatar Yorick Peterse

Unify detecting of special repository files

This moves the logic of detecting special repository files (e.g. a
README or a Koding configuration file) to a single class:
Gitlab::FileDetector. Moving this logic into a single place allows this
to be re-used more easily.

This commit also changes Repository#gitlab_ci_yaml so that its cached
similar to other data (e.g. the Koding configuration file).
parent b98193c5
......@@ -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
......
......@@ -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)
......
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
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
......@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment