From 636e9bdd11bdb60d9711c3d3ba66406b6d8967ae Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 7 Feb 2017 14:19:12 -0600 Subject: Support a string source in the route map --- doc/ci/environments.md | 20 ++++++++++++++++---- lib/gitlab/route_map.rb | 32 +++++++++++++++----------------- spec/lib/gitlab/route_map_spec.rb | 20 ++++++++++---------- 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/doc/ci/environments.md b/doc/ci/environments.md index f00cc854b05..cb62ed723f0 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -459,6 +459,10 @@ A Route Map is a file inside the repository at `.gitlab/route-map.yml`, which co This is an example of a route map for [Middleman](https://middlemanapp.com) static websites like [http://about.gitlab.com](https://gitlab.com/gitlab-com/www-gitlab-com): ```yaml +# Team data +- source: 'data/team.yml' # data/team.yml + public: 'team/' # team/ + # Blogposts - source: /source\/posts\/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.+?)\..*/ # source/posts/2017-01-30-around-the-world-in-6-releases.html.md.erb public: '\1/\2/\3/\4/' # 2017/01/30/around-the-world-in-6-releases/ @@ -474,10 +478,18 @@ This is an example of a route map for [Middleman](https://middlemanapp.com) stat Mappings are defined as entries in the root YAML array, and are identified by a `-` prefix. Within an entry, we have a hash map with two keys: -- `source`: a regular expression, starting and ending with `/`. Can include capture groups denoted by `()` that can be referred to in the `public` path. Slashes (`/`) can, but don't have to be, escaped as `\/`. -- `public`: a string, starting and ending with `'`. Can include `\N` expressions to refer to capture groups in the `source` regular expression in order of their occurence, starting with `\1`. - -The public path for a source path is determined by finding the first `source` expression that matches it, and returning the corresponding `public` path, replacing the `\N` expressions with the values of the `()` capture groups. +- `source` + - a string, starting and ending with `'`, for an exact match + - a regular expression, starting and ending with `/`, for a pattern match + - The regular expression needs to match the entire source path - `^` and `$` anchors are implied. + - Can include capture groups denoted by `()` that can be referred to in the `public` path. + - Slashes (`/`) can, but don't have to, be escaped as `\/`. + - Literal periods (`.`) should be escaped as `\.`. +- `public` + - a string, starting and ending with `'`. + - Can include `\N` expressions to refer to capture groups in the `source` regular expression in order of their occurence, starting with `\1`. + +The public path for a source path is determined by finding the first `source` expression that matches it, and returning the corresponding `public` path, replacing the `\N` expressions with the values of the `()` capture groups if appropriate. In the example above, the fact that mappings are evaluated in order of their definition is used to ensure that `source/index.html.haml` will match `/source\/(.+?\.html).*/` instead of `/source\/(.*)/`, and will result in a public path of `index.html`, instead of `index.html.haml`. diff --git a/lib/gitlab/route_map.rb b/lib/gitlab/route_map.rb index 89985d90c10..f96c30b3e33 100644 --- a/lib/gitlab/route_map.rb +++ b/lib/gitlab/route_map.rb @@ -6,16 +6,16 @@ module Gitlab begin entries = YAML.safe_load(data) rescue - raise FormatError, 'Route map needs to be valid YAML' + raise FormatError, 'Route map is not valid YAML' end - raise FormatError, 'Route map needs to be an array' unless entries.is_a?(Array) + raise FormatError, 'Route map is not an array' unless entries.is_a?(Array) @map = entries.map { |entry| parse_entry(entry) } end def public_path_for_source_path(path) - mapping = @map.find { |mapping| path =~ mapping[:source] } + mapping = @map.find { |mapping| mapping[:source] === path } return unless mapping path.sub(mapping[:source], mapping[:public]) @@ -24,27 +24,25 @@ module Gitlab private def parse_entry(entry) - raise FormatError, 'Route map entry needs to be a hash' unless entry.is_a?(Hash) - raise FormatError, 'Route map entry requires a source key' unless entry.has_key?('source') - raise FormatError, 'Route map entry requires a public key' unless entry.has_key?('public') + raise FormatError, 'Route map entry is not a hash' unless entry.is_a?(Hash) + raise FormatError, 'Route map entry does not have a source key' unless entry.has_key?('source') + raise FormatError, 'Route map entry does not have a public key' unless entry.has_key?('public') - source_regexp = entry['source'] + source_pattern = entry['source'] public_path = entry['public'] - unless source_regexp.start_with?('/') && source_regexp.end_with?('/') - raise FormatError, 'Route map entry source needs to start and end in a slash (/)' - end - - source_regexp = source_regexp[1...-1].gsub('\/', '/') + if source_pattern.start_with?('/') && source_pattern.end_with?('/') + source_pattern = source_pattern[1...-1].gsub('\/', '/') - begin - source_regexp = Regexp.new("^#{source_regexp}$") - rescue RegexpError => e - raise FormatError, "Route map entry source needs to be a valid regular expression: #{e}" + begin + source_pattern = Regexp.new("^#{source_pattern}$") + rescue RegexpError => e + raise FormatError, "Route map entry source is not a valid regular expression: #{e}" + end end { - source: source_regexp, + source: source_pattern, public: public_path } end diff --git a/spec/lib/gitlab/route_map_spec.rb b/spec/lib/gitlab/route_map_spec.rb index 459fa8a63a9..2370f56a613 100644 --- a/spec/lib/gitlab/route_map_spec.rb +++ b/spec/lib/gitlab/route_map_spec.rb @@ -37,13 +37,6 @@ describe Gitlab::RouteMap, lib: true do end end - context 'when an entry source does not start and end with a slash' do - it 'raises an error' do - expect { described_class.new(YAML.dump([{ 'source' => 'index.html', 'public' => 'index.html' }])) }. - to raise_error(Gitlab::RouteMap::FormatError, /a slash/) - end - end - context 'when an entry source is not a valid regex' do it 'raises an error' do expect { described_class.new(YAML.dump([{ 'source' => '/[/', 'public' => 'index.html' }])) }. @@ -53,9 +46,10 @@ describe Gitlab::RouteMap, lib: true do context 'when all is good' do it 'returns a route map' do - route_map = described_class.new(YAML.dump([{ 'source' => '/index\.html/', 'public' => 'index.html' }])) - - expect(route_map.public_path_for_source_path('index.html')).to eq('index.html') + route_map = described_class.new(YAML.dump([{ 'source' => 'index.haml', 'public' => 'index.html' }, { 'source' => '/(.*)\.md/', 'public' => '\1.html' }])) + + expect(route_map.public_path_for_source_path('index.haml')).to eq('index.html') + expect(route_map.public_path_for_source_path('foo.md')).to eq('foo.html') end end end @@ -63,6 +57,10 @@ describe Gitlab::RouteMap, lib: true do describe '#public_path_for_source_path' do subject do described_class.new(<<-'MAP'.strip_heredoc) + # Team data + - source: 'data/team.yml' + public: 'team/' + # Blogposts - source: /source/posts/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.+?)\..*/ # source/posts/2017-01-30-around-the-world-in-6-releases.html.md.erb public: '\1/\2/\3/\4/' # 2017/01/30/around-the-world-in-6-releases/ @@ -78,6 +76,8 @@ describe Gitlab::RouteMap, lib: true do end it 'returns the public path for a provided source path' do + expect(subject.public_path_for_source_path('data/team.yml')).to eq('team/') + expect(subject.public_path_for_source_path('source/posts/2017-01-30-around-the-world-in-6-releases.html.md.erb')).to eq('2017/01/30/around-the-world-in-6-releases/') expect(subject.public_path_for_source_path('source/index.html.haml')).to eq('index.html') -- cgit v1.2.1