From aa24feed6fa0e24d1e78d38b3f26b4bd26b016b8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:25:57 -0500 Subject: Refactor DependencyLinker::BaseLinker --- lib/gitlab/dependency_linker/base_linker.rb | 58 ++++------------------ lib/gitlab/dependency_linker/gemfile_linker.rb | 23 +++++---- lib/gitlab/dependency_linker/method_linker.rb | 48 ++++++++++++++++++ .../dependency_linker/gemfile_linker_spec.rb | 2 +- spec/lib/gitlab/highlight_spec.rb | 2 - 5 files changed, 71 insertions(+), 62 deletions(-) create mode 100644 lib/gitlab/dependency_linker/method_linker.rb diff --git a/lib/gitlab/dependency_linker/base_linker.rb b/lib/gitlab/dependency_linker/base_linker.rb index 5f4027e7e81..019e670081e 100644 --- a/lib/gitlab/dependency_linker/base_linker.rb +++ b/lib/gitlab/dependency_linker/base_linker.rb @@ -1,6 +1,8 @@ module Gitlab module DependencyLinker class BaseLinker + URL_REGEX = %r{https?://[^'"]+}.freeze + class_attribute :file_type def self.support?(blob_name) @@ -26,59 +28,17 @@ module Gitlab private - def package_url(name) - raise NotImplementedError - end - def link_dependencies raise NotImplementedError end - def package_link(name, url = package_url(name)) - return name unless url - - %{#{ERB::Util.html_escape_once(name)}} + def license_url(name) + Licensee::License.find(name)&.url end - # Links package names in a method call or assignment string argument. - # - # Example: - # link_method_call("gem") - # # Will link `package` in `gem "package"`, `gem("package")` and `gem = "package"` - # - # link_method_call("gem", "specific_package") - # # Will link `specific_package` in `gem "specific_package"` - # - # link_method_call("github", /[^\/]+\/[^\/]+/) - # # Will link `user/repo` in `github "user/repo"`, but not `github "package"` - # - # link_method_call(%w[add_dependency add_development_dependency]) - # # Will link `spec.add_dependency "package"` and `spec.add_development_dependency "package"` - # - # link_method_call("name") - # # Will link `package` in `self.name = "package"` - def link_method_call(method_names, value = nil, &url_proc) - value = - case value - when String - Regexp.escape(value) - when nil - /[^'"]+/ - else - value - end - - method_names = Array(method_names).map { |name| Regexp.escape(name) } - - regex = %r{ - #{Regexp.union(method_names)} # Method name - \s* # Whitespace - [(=]? # Opening brace or equals sign - \s* # Whitespace - ['"](?#{value})['"] # Package name in quotes - }x - link_regex(regex, &url_proc) + def link_tag(name, url) + %{#{ERB::Util.html_escape_once(name)}} end # Links package names based on regex. @@ -86,13 +46,13 @@ module Gitlab # Example: # link_regex(/(github:|:github =>)\s*['"](?[^'"]+)['"]/) # # Will link `user/repo` in `github: "user/repo"` or `:github => "user/repo"` - def link_regex(regex) + def link_regex(regex, &url_proc) highlighted_lines.map!.with_index do |rich_line, i| marker = StringRegexMarker.new(plain_lines[i], rich_line.html_safe) marker.mark(regex, group: :name) do |text, left:, right:| - url = block_given? ? yield(text) : package_url(text) - package_link(text, url) + url = yield(text) + url ? link_tag(text, url) : text end end end diff --git a/lib/gitlab/dependency_linker/gemfile_linker.rb b/lib/gitlab/dependency_linker/gemfile_linker.rb index 9b82e126528..d034ea67387 100644 --- a/lib/gitlab/dependency_linker/gemfile_linker.rb +++ b/lib/gitlab/dependency_linker/gemfile_linker.rb @@ -1,28 +1,31 @@ module Gitlab module DependencyLinker - class GemfileLinker < BaseLinker + class GemfileLinker < MethodLinker self.file_type = :gemfile private def link_dependencies - # Link `gem "package_name"` to https://rubygems.org/gems/package_name - link_method_call("gem") + link_urls + link_packages + end + def link_urls # Link `github: "user/repo"` to https://github.com/user/repo - link_regex(/(github:|:github\s*=>)\s*['"](?[^'"]+)['"]/) do |name| - "https://github.com/#{name}" - end + link_regex(/(github:|:github\s*=>)\s*['"](?[^'"]+)['"]/, &method(:github_url)) # Link `git: "https://gitlab.example.com/user/repo"` to https://gitlab.example.com/user/repo - link_regex(%r{(git:|:git\s*=>)\s*['"](?https?://[^'"]+)['"]}) { |url| url } + link_regex(%r{(git:|:git\s*=>)\s*['"](?#{URL_REGEX})['"]}, &:itself) # Link `source "https://rubygems.org"` to https://rubygems.org - link_method_call("source", %r{https?://[^'"]+}) { |url| url } + link_method_call('source', URL_REGEX, &:itself) end - def package_url(name) - "https://rubygems.org/gems/#{name}" + def link_packages + # Link `gem "package_name"` to https://rubygems.org/gems/package_name + link_method_call('gem') do |name| + "https://rubygems.org/gems/#{name}" + end end end end diff --git a/lib/gitlab/dependency_linker/method_linker.rb b/lib/gitlab/dependency_linker/method_linker.rb new file mode 100644 index 00000000000..eeb7c1587c0 --- /dev/null +++ b/lib/gitlab/dependency_linker/method_linker.rb @@ -0,0 +1,48 @@ +module Gitlab + module DependencyLinker + class MethodLinker < BaseLinker + private + + # Links package names in a method call or assignment string argument. + # + # Example: + # link_method_call('gem') + # # Will link `package` in `gem "package"`, `gem("package")` and `gem = "package"` + # + # link_method_call('gem', 'specific_package') + # # Will link `specific_package` in `gem "specific_package"` + # + # link_method_call('github', /[^\/"]+\/[^\/"]+/) + # # Will link `user/repo` in `github "user/repo"`, but not `github "package"` + # + # link_method_call(%w[add_dependency add_development_dependency]) + # # Will link `spec.add_dependency "package"` and `spec.add_development_dependency "package"` + # + # link_method_call('name') + # # Will link `package` in `self.name = "package"` + def link_method_call(method_names, value = nil, &url_proc) + method_names = Array(method_names).map { |name| Regexp.escape(name) } + + value = + case value + when String + Regexp.escape(value) + when nil + /[^'"]+/ + else + value + end + + regex = %r{ + #{Regexp.union(method_names)} # Method name + \s* # Whitespace + [(=]? # Opening brace or equals sign + \s* # Whitespace + ['"](?#{value})['"] # Package name in quotes + }x + + link_regex(regex, &url_proc) + end + end + end +end diff --git a/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb b/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb index 2e52097a946..3f8335f03ea 100644 --- a/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb @@ -33,7 +33,7 @@ describe Gitlab::DependencyLinker::GemfileLinker, lib: true do subject { Gitlab::Highlight.highlight(file_name, file_content) } def link(name, url) - %{#{name}} + %{#{name}} end it 'links sources' do diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb index e57b3053871..a20cef3b000 100644 --- a/spec/lib/gitlab/highlight_spec.rb +++ b/spec/lib/gitlab/highlight_spec.rb @@ -59,8 +59,6 @@ describe Gitlab::Highlight, lib: true do end describe '#highlight' do - subject { described_class.highlight(file_name, file_content, nowrap: false) } - it 'links dependencies via DependencyLinker' do expect(Gitlab::DependencyLinker).to receive(:link). with('file.name', 'Contents', anything).and_call_original -- cgit v1.2.1 From 02ad8c0c68b575ca4957e0a478714ef2144a9b8a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:26:31 -0500 Subject: Autolink package names in gemspec --- lib/gitlab/dependency_linker.rb | 3 +- lib/gitlab/dependency_linker/gemspec_linker.rb | 18 ++++++ .../dependency_linker/gemspec_linker_spec.rb | 66 ++++++++++++++++++++++ spec/lib/gitlab/dependency_linker_spec.rb | 8 +++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 lib/gitlab/dependency_linker/gemspec_linker.rb create mode 100644 spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb diff --git a/lib/gitlab/dependency_linker.rb b/lib/gitlab/dependency_linker.rb index c45ae8feb2c..14fc506fbb7 100644 --- a/lib/gitlab/dependency_linker.rb +++ b/lib/gitlab/dependency_linker.rb @@ -1,7 +1,8 @@ module Gitlab module DependencyLinker LINKERS = [ - GemfileLinker + GemfileLinker, + GemspecLinker, ].freeze def self.linker(blob_name) diff --git a/lib/gitlab/dependency_linker/gemspec_linker.rb b/lib/gitlab/dependency_linker/gemspec_linker.rb new file mode 100644 index 00000000000..f1783ee2ab4 --- /dev/null +++ b/lib/gitlab/dependency_linker/gemspec_linker.rb @@ -0,0 +1,18 @@ +module Gitlab + module DependencyLinker + class GemspecLinker < MethodLinker + self.file_type = :gemspec + + private + + def link_dependencies + link_method_call('homepage', URL_REGEX, &:itself) + link_method_call('license', &method(:license_url)) + + link_method_call(%w[name add_dependency add_runtime_dependency add_development_dependency]) do |name| + "https://rubygems.org/gems/#{name}" + end + end + end + end +end diff --git a/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb b/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb new file mode 100644 index 00000000000..d4a71403939 --- /dev/null +++ b/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb @@ -0,0 +1,66 @@ +require 'rails_helper' + +describe Gitlab::DependencyLinker::GemspecLinker, lib: true do + describe '.support?' do + it 'supports *.gemspec' do + expect(described_class.support?('gitlab_git.gemspec')).to be_truthy + end + + it 'does not support other files' do + expect(described_class.support?('.gemspec.example')).to be_falsey + end + end + + describe '#link' do + let(:file_name) { "gitlab_git.gemspec" } + + let(:file_content) do + <<-CONTENT.strip_heredoc + Gem::Specification.new do |s| + s.name = 'gitlab_git' + s.version = `cat VERSION` + s.date = Time.now.strftime('%Y-%m-%d') + s.summary = "Gitlab::Git library" + s.description = "GitLab wrapper around git objects" + s.authors = ["Dmitriy Zaporozhets"] + s.email = 'dmitriy.zaporozhets@gmail.com' + s.license = 'MIT' + s.files = `git ls-files lib/`.split('\n') << 'VERSION' + s.homepage = 'https://gitlab.com/gitlab-org/gitlab_git' + + s.add_dependency('github-linguist', '~> 4.7.0') + s.add_dependency('activesupport', '~> 4.0') + s.add_dependency('rugged', '~> 0.24.0') + s.add_runtime_dependency('charlock_holmes', '~> 0.7.3') + s.add_development_dependency('listen', '~> 3.0.6') + end + CONTENT + end + + subject { Gitlab::Highlight.highlight(file_name, file_content) } + + def link(name, url) + %{#{name}} + end + + it 'links the gem name' do + expect(subject).to include(link('gitlab_git', 'https://rubygems.org/gems/gitlab_git')) + end + + it 'links the license' do + expect(subject).to include(link('MIT', 'http://choosealicense.com/licenses/mit/')) + end + + it 'links the homepage' do + expect(subject).to include(link('https://gitlab.com/gitlab-org/gitlab_git', 'https://gitlab.com/gitlab-org/gitlab_git')) + end + + it 'links dependencies' do + expect(subject).to include(link('github-linguist', 'https://rubygems.org/gems/github-linguist')) + expect(subject).to include(link('activesupport', 'https://rubygems.org/gems/activesupport')) + expect(subject).to include(link('rugged', 'https://rubygems.org/gems/rugged')) + expect(subject).to include(link('charlock_holmes', 'https://rubygems.org/gems/charlock_holmes')) + expect(subject).to include(link('listen', 'https://rubygems.org/gems/listen')) + end + end +end diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb index 03d5b61d70c..7ba987026a4 100644 --- a/spec/lib/gitlab/dependency_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker_spec.rb @@ -9,5 +9,13 @@ describe Gitlab::DependencyLinker, lib: true do described_class.link(blob_name, nil, nil) end + + it 'links using GemspecLinker' do + blob_name = 'gitlab_git.gemspec' + + expect(described_class::GemspecLinker).to receive(:link) + + described_class.link(blob_name, nil, nil) + end end end -- cgit v1.2.1 From 83747783e25b2d8b60efa2cb1aa9d6bd823fcdfe Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:27:50 -0500 Subject: Autolink package names in package.json --- lib/gitlab/dependency_linker.rb | 1 + lib/gitlab/dependency_linker/base_linker.rb | 4 + lib/gitlab/dependency_linker/json_linker.rb | 63 ++++++++++++++++ .../dependency_linker/package_json_linker.rb | 44 +++++++++++ .../dependency_linker/package_json_linker_spec.rb | 85 ++++++++++++++++++++++ spec/lib/gitlab/dependency_linker_spec.rb | 8 ++ 6 files changed, 205 insertions(+) create mode 100644 lib/gitlab/dependency_linker/json_linker.rb create mode 100644 lib/gitlab/dependency_linker/package_json_linker.rb create mode 100644 spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb diff --git a/lib/gitlab/dependency_linker.rb b/lib/gitlab/dependency_linker.rb index 14fc506fbb7..88f4ebedb9f 100644 --- a/lib/gitlab/dependency_linker.rb +++ b/lib/gitlab/dependency_linker.rb @@ -3,6 +3,7 @@ module Gitlab LINKERS = [ GemfileLinker, GemspecLinker, + PackageJsonLinker, ].freeze def self.linker(blob_name) diff --git a/lib/gitlab/dependency_linker/base_linker.rb b/lib/gitlab/dependency_linker/base_linker.rb index 019e670081e..15fa9f40116 100644 --- a/lib/gitlab/dependency_linker/base_linker.rb +++ b/lib/gitlab/dependency_linker/base_linker.rb @@ -2,6 +2,7 @@ module Gitlab module DependencyLinker class BaseLinker URL_REGEX = %r{https?://[^'"]+}.freeze + REPO_REGEX = %r{[^/'"]+/[^/'"]+}.freeze class_attribute :file_type @@ -36,6 +37,9 @@ module Gitlab Licensee::License.find(name)&.url end + def github_url(name) + "https://github.com/#{name}" + end def link_tag(name, url) %{#{ERB::Util.html_escape_once(name)}} diff --git a/lib/gitlab/dependency_linker/json_linker.rb b/lib/gitlab/dependency_linker/json_linker.rb new file mode 100644 index 00000000000..021347b6429 --- /dev/null +++ b/lib/gitlab/dependency_linker/json_linker.rb @@ -0,0 +1,63 @@ +module Gitlab + module DependencyLinker + class JsonLinker < BaseLinker + def link + return highlighted_text unless json + + super + end + + private + + # Links package names in a JSON key or values. + # + # Example: + # link_json('name') + # # Will link `package` in `"name": "package"` + # + # link_json('name', 'specific_package') + # # Will link `specific_package` in `"name": "specific_package"` + # + # link_json('name', /[^\/]+\/[^\/]+/) + # # Will link `user/repo` in `"name": "user/repo"`, but not `"name": "package"` + # + # link_json('specific_package', '1.0.1', link: :key) + # # Will link `specific_package` in `"specific_package": "1.0.1"` + def link_json(key, value = nil, link: :value, &url_proc) + key = + case key + when Array + Regexp.union(key.map { |name| Regexp.escape(name) }) + when String + Regexp.escape(key) + when nil + '[^"]+' + else + key + end + + value = + case value + when String + Regexp.escape(value) + when nil + '[^"]+' + else + value + end + + if link == :value + value = "(?#{value})" + else + key = "(?#{key})" + end + + link_regex(/"#{key}":\s*"#{value}"/, &url_proc) + end + + def json + @json ||= JSON.parse(plain_text) rescue nil + end + end + end +end diff --git a/lib/gitlab/dependency_linker/package_json_linker.rb b/lib/gitlab/dependency_linker/package_json_linker.rb new file mode 100644 index 00000000000..330c95f0880 --- /dev/null +++ b/lib/gitlab/dependency_linker/package_json_linker.rb @@ -0,0 +1,44 @@ +module Gitlab + module DependencyLinker + class PackageJsonLinker < JsonLinker + self.file_type = :package_json + + private + + def link_dependencies + link_json('name', json["name"], &method(:package_url)) + link_json('license', &method(:license_url)) + link_json(%w[homepage url], URL_REGEX, &:itself) + + link_packages + end + + def link_packages + link_packages_at_key("dependencies", &method(:package_url)) + link_packages_at_key("devDependencies", &method(:package_url)) + end + + def link_packages_at_key(key, &url_proc) + dependencies = json[key] + return unless dependencies + + dependencies.each do |name, version| + link_json(name, version, link: :key, &url_proc) + + link_json(name) do |value| + case value + when /\A#{URL_REGEX}\z/ + value + when /\A#{REPO_REGEX}\z/ + github_url(value) + end + end + end + end + + def package_url(name) + "https://npmjs.com/package/#{name}" + end + end + end +end diff --git a/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb new file mode 100644 index 00000000000..b5f2c387d6f --- /dev/null +++ b/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb @@ -0,0 +1,85 @@ +require 'rails_helper' + +describe Gitlab::DependencyLinker::PackageJsonLinker, lib: true do + describe '.support?' do + it 'supports package.json' do + expect(described_class.support?('package.json')).to be_truthy + end + + it 'does not support other files' do + expect(described_class.support?('package.json.example')).to be_falsey + end + end + + describe '#link' do + let(:file_name) { "package.json" } + + let(:file_content) do + <<-CONTENT.strip_heredoc + { + "name": "module-name", + "version": "10.3.1", + "repository": { + "type": "git", + "url": "https://github.com/vuejs/vue.git" + }, + "homepage": "https://github.com/vuejs/vue#readme", + "dependencies": { + "primus": "*", + "async": "~0.8.0", + "express": "4.2.x", + "bigpipe": "bigpipe/pagelet", + "plates": "https://github.com/flatiron/plates/tarball/master" + }, + "devDependencies": { + "vows": "^0.7.0", + "assume": "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0", + "pre-commit": "*" + }, + "license": "MIT" + } + CONTENT + end + + subject { Gitlab::Highlight.highlight(file_name, file_content) } + + def link(name, url) + %{#{name}} + end + + it 'links the module name' do + expect(subject).to include(link('module-name', 'https://npmjs.com/package/module-name')) + end + + it 'links the homepage' do + expect(subject).to include(link('https://github.com/vuejs/vue#readme', 'https://github.com/vuejs/vue#readme')) + end + + it 'links the repository URL' do + expect(subject).to include(link('https://github.com/vuejs/vue.git', 'https://github.com/vuejs/vue.git')) + end + + it 'links the license' do + expect(subject).to include(link('MIT', 'http://choosealicense.com/licenses/mit/')) + end + + it 'links dependencies' do + expect(subject).to include(link('primus', 'https://npmjs.com/package/primus')) + expect(subject).to include(link('async', 'https://npmjs.com/package/async')) + expect(subject).to include(link('express', 'https://npmjs.com/package/express')) + expect(subject).to include(link('bigpipe', 'https://npmjs.com/package/bigpipe')) + expect(subject).to include(link('plates', 'https://npmjs.com/package/plates')) + expect(subject).to include(link('vows', 'https://npmjs.com/package/vows')) + expect(subject).to include(link('assume', 'https://npmjs.com/package/assume')) + expect(subject).to include(link('pre-commit', 'https://npmjs.com/package/pre-commit')) + end + + it 'links GitHub repos' do + expect(subject).to include(link('bigpipe/pagelet', 'https://github.com/bigpipe/pagelet')) + end + + it 'links Git repos' do + expect(subject).to include(link('https://github.com/flatiron/plates/tarball/master', 'https://github.com/flatiron/plates/tarball/master')) + end + end +end diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb index 7ba987026a4..53960b0a662 100644 --- a/spec/lib/gitlab/dependency_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker_spec.rb @@ -17,5 +17,13 @@ describe Gitlab::DependencyLinker, lib: true do described_class.link(blob_name, nil, nil) end + + it 'links using PackageJsonLinker' do + blob_name = 'package.json' + + expect(described_class::PackageJsonLinker).to receive(:link) + + described_class.link(blob_name, nil, nil) + end end end -- cgit v1.2.1 From fbec6dbd1a120cd111608c637cd63c8313aeb976 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:28:11 -0500 Subject: Autolink package names in composer.json --- lib/gitlab/dependency_linker.rb | 1 + .../dependency_linker/composer_json_linker.rb | 18 +++++ .../dependency_linker/composer_json_linker_spec.rb | 82 ++++++++++++++++++++++ spec/lib/gitlab/dependency_linker_spec.rb | 8 +++ 4 files changed, 109 insertions(+) create mode 100644 lib/gitlab/dependency_linker/composer_json_linker.rb create mode 100644 spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb diff --git a/lib/gitlab/dependency_linker.rb b/lib/gitlab/dependency_linker.rb index 88f4ebedb9f..9cd2fbdfd17 100644 --- a/lib/gitlab/dependency_linker.rb +++ b/lib/gitlab/dependency_linker.rb @@ -4,6 +4,7 @@ module Gitlab GemfileLinker, GemspecLinker, PackageJsonLinker, + ComposerJsonLinker, ].freeze def self.linker(blob_name) diff --git a/lib/gitlab/dependency_linker/composer_json_linker.rb b/lib/gitlab/dependency_linker/composer_json_linker.rb new file mode 100644 index 00000000000..0245bf4077a --- /dev/null +++ b/lib/gitlab/dependency_linker/composer_json_linker.rb @@ -0,0 +1,18 @@ +module Gitlab + module DependencyLinker + class ComposerJsonLinker < PackageJsonLinker + self.file_type = :composer_json + + private + + def link_packages + link_packages_at_key("require", &method(:package_url)) + link_packages_at_key("require-dev", &method(:package_url)) + end + + def package_url(name) + "https://packagist.org/packages/#{name}" if name =~ %r{\A#{REPO_REGEX}\z} + end + end + end +end diff --git a/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb new file mode 100644 index 00000000000..d7a926e800f --- /dev/null +++ b/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb @@ -0,0 +1,82 @@ +require 'rails_helper' + +describe Gitlab::DependencyLinker::ComposerJsonLinker, lib: true do + describe '.support?' do + it 'supports composer.json' do + expect(described_class.support?('composer.json')).to be_truthy + end + + it 'does not support other files' do + expect(described_class.support?('composer.json.example')).to be_falsey + end + end + + describe '#link' do + let(:file_name) { "composer.json" } + + let(:file_content) do + <<-CONTENT.strip_heredoc + { + "name": "laravel/laravel", + "homepage": "https://laravel.com/", + "description": "The Laravel Framework.", + "keywords": ["framework", "laravel"], + "license": "MIT", + "type": "project", + "repositories": [ + { + "type": "git", + "url": "https://github.com/laravel/laravel.git" + } + ], + "require": { + "php": ">=5.5.9", + "laravel/framework": "5.2.*" + }, + "require-dev": { + "fzaninotto/faker": "~1.4", + "mockery/mockery": "0.9.*", + "phpunit/phpunit": "~4.0", + "symfony/css-selector": "2.8.*|3.0.*", + "symfony/dom-crawler": "2.8.*|3.0.*" + } + } + CONTENT + end + + subject { Gitlab::Highlight.highlight(file_name, file_content) } + + def link(name, url) + %{#{name}} + end + + it 'links the module name' do + expect(subject).to include(link('laravel/laravel', 'https://packagist.org/packages/laravel/laravel')) + end + + it 'links the homepage' do + expect(subject).to include(link('https://laravel.com/', 'https://laravel.com/')) + end + + it 'links the repository URL' do + expect(subject).to include(link('https://github.com/laravel/laravel.git', 'https://github.com/laravel/laravel.git')) + end + + it 'links the license' do + expect(subject).to include(link('MIT', 'http://choosealicense.com/licenses/mit/')) + end + + it 'links dependencies' do + expect(subject).to include(link('laravel/framework', 'https://packagist.org/packages/laravel/framework')) + expect(subject).to include(link('fzaninotto/faker', 'https://packagist.org/packages/fzaninotto/faker')) + expect(subject).to include(link('mockery/mockery', 'https://packagist.org/packages/mockery/mockery')) + expect(subject).to include(link('phpunit/phpunit', 'https://packagist.org/packages/phpunit/phpunit')) + expect(subject).to include(link('symfony/css-selector', 'https://packagist.org/packages/symfony/css-selector')) + expect(subject).to include(link('symfony/dom-crawler', 'https://packagist.org/packages/symfony/dom-crawler')) + end + + it 'does not link core dependencies' do + expect(subject).not_to include(link('php', 'https://packagist.org/packages/php')) + end + end +end diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb index 53960b0a662..7493e747e01 100644 --- a/spec/lib/gitlab/dependency_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker_spec.rb @@ -25,5 +25,13 @@ describe Gitlab::DependencyLinker, lib: true do described_class.link(blob_name, nil, nil) end + + it 'links using ComposerJsonLinker' do + blob_name = 'composer.json' + + expect(described_class::ComposerJsonLinker).to receive(:link) + + described_class.link(blob_name, nil, nil) + end end end -- cgit v1.2.1 From 03db4f110613c73003f86d0ff98a7dcb6f0dae59 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:28:52 -0500 Subject: Autolink package names in Podfile --- lib/gitlab/dependency_linker.rb | 1 + lib/gitlab/dependency_linker/cocoapods.rb | 10 ++++ lib/gitlab/dependency_linker/podfile_linker.rb | 15 ++++++ .../dependency_linker/podfile_linker_spec.rb | 53 ++++++++++++++++++++++ spec/lib/gitlab/dependency_linker_spec.rb | 8 ++++ 5 files changed, 87 insertions(+) create mode 100644 lib/gitlab/dependency_linker/cocoapods.rb create mode 100644 lib/gitlab/dependency_linker/podfile_linker.rb create mode 100644 spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb diff --git a/lib/gitlab/dependency_linker.rb b/lib/gitlab/dependency_linker.rb index 9cd2fbdfd17..44358d2634b 100644 --- a/lib/gitlab/dependency_linker.rb +++ b/lib/gitlab/dependency_linker.rb @@ -5,6 +5,7 @@ module Gitlab GemspecLinker, PackageJsonLinker, ComposerJsonLinker, + PodfileLinker, ].freeze def self.linker(blob_name) diff --git a/lib/gitlab/dependency_linker/cocoapods.rb b/lib/gitlab/dependency_linker/cocoapods.rb new file mode 100644 index 00000000000..2fbde7da1b4 --- /dev/null +++ b/lib/gitlab/dependency_linker/cocoapods.rb @@ -0,0 +1,10 @@ +module Gitlab + module DependencyLinker + module Cocoapods + def package_url(name) + package = name.split("/", 2).first + "https://cocoapods.org/pods/#{package}" + end + end + end +end diff --git a/lib/gitlab/dependency_linker/podfile_linker.rb b/lib/gitlab/dependency_linker/podfile_linker.rb new file mode 100644 index 00000000000..60ad166ea17 --- /dev/null +++ b/lib/gitlab/dependency_linker/podfile_linker.rb @@ -0,0 +1,15 @@ +module Gitlab + module DependencyLinker + class PodfileLinker < GemfileLinker + include Cocoapods + + self.file_type = :podfile + + private + + def link_packages + link_method_call('pod', &method(:package_url)) + end + end + end +end diff --git a/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb b/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb new file mode 100644 index 00000000000..06007cf97f7 --- /dev/null +++ b/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb @@ -0,0 +1,53 @@ +require 'rails_helper' + +describe Gitlab::DependencyLinker::PodfileLinker, lib: true do + describe '.support?' do + it 'supports Podfile' do + expect(described_class.support?('Podfile')).to be_truthy + end + + it 'does not support other files' do + expect(described_class.support?('Podfile.lock')).to be_falsey + end + end + + describe '#link' do + let(:file_name) { "Podfile" } + + let(:file_content) do + <<-CONTENT.strip_heredoc + source 'https://github.com/artsy/Specs.git' + source 'https://github.com/CocoaPods/Specs.git' + + platform :ios, '8.0' + use_frameworks! + inhibit_all_warnings! + + target 'Artsy' do + pod 'AFNetworking', "~> 2.5" + pod 'Interstellar/Core', git: 'https://github.com/ashfurrow/Interstellar.git', branch: 'observable-unsubscribe' + end + CONTENT + end + + subject { Gitlab::Highlight.highlight(file_name, file_content) } + + def link(name, url) + %{#{name}} + end + + it 'links sources' do + expect(subject).to include(link('https://github.com/artsy/Specs.git', 'https://github.com/artsy/Specs.git')) + expect(subject).to include(link('https://github.com/CocoaPods/Specs.git', 'https://github.com/CocoaPods/Specs.git')) + end + + it 'links packages' do + expect(subject).to include(link('AFNetworking', 'https://cocoapods.org/pods/AFNetworking')) + expect(subject).to include(link('Interstellar/Core', 'https://cocoapods.org/pods/Interstellar')) + end + + it 'links Git repos' do + expect(subject).to include(link('https://github.com/ashfurrow/Interstellar.git', 'https://github.com/ashfurrow/Interstellar.git')) + end + end +end diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb index 7493e747e01..104e3a32e6c 100644 --- a/spec/lib/gitlab/dependency_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker_spec.rb @@ -33,5 +33,13 @@ describe Gitlab::DependencyLinker, lib: true do described_class.link(blob_name, nil, nil) end + + it 'links using PodfileLinker' do + blob_name = 'Podfile' + + expect(described_class::PodfileLinker).to receive(:link) + + described_class.link(blob_name, nil, nil) + end end end -- cgit v1.2.1 From 190b7b45a605475668a089a0ce43f3d89b9d7c47 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:29:13 -0500 Subject: Autolink package names in podspec --- lib/gitlab/dependency_linker.rb | 1 + lib/gitlab/dependency_linker/podspec_linker.rb | 24 ++++++++ .../dependency_linker/podspec_linker_spec.rb | 69 ++++++++++++++++++++++ spec/lib/gitlab/dependency_linker_spec.rb | 8 +++ 4 files changed, 102 insertions(+) create mode 100644 lib/gitlab/dependency_linker/podspec_linker.rb create mode 100644 spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb diff --git a/lib/gitlab/dependency_linker.rb b/lib/gitlab/dependency_linker.rb index 44358d2634b..ffd6ab8e2f8 100644 --- a/lib/gitlab/dependency_linker.rb +++ b/lib/gitlab/dependency_linker.rb @@ -6,6 +6,7 @@ module Gitlab PackageJsonLinker, ComposerJsonLinker, PodfileLinker, + PodspecLinker, ].freeze def self.linker(blob_name) diff --git a/lib/gitlab/dependency_linker/podspec_linker.rb b/lib/gitlab/dependency_linker/podspec_linker.rb new file mode 100644 index 00000000000..a52c7a02439 --- /dev/null +++ b/lib/gitlab/dependency_linker/podspec_linker.rb @@ -0,0 +1,24 @@ +module Gitlab + module DependencyLinker + class PodspecLinker < MethodLinker + include Cocoapods + + STRING_REGEX = /['"](?[^'"]+)['"]/.freeze + + self.file_type = :podspec + + private + + def link_dependencies + link_method_call('homepage', URL_REGEX, &:itself) + + link_regex(%r{(git:|:git\s*=>)\s*['"](?#{URL_REGEX})['"]}, &:itself) + + link_method_call('license', &method(:license_url)) + link_regex(/license\s*=\s*\{\s*(type:|:type\s*=>)\s*#{STRING_REGEX}/, &method(:license_url)) + + link_method_call(%w[name dependency], &method(:package_url)) + end + end + end +end diff --git a/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb b/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb new file mode 100644 index 00000000000..dfc366b5817 --- /dev/null +++ b/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb @@ -0,0 +1,69 @@ +require 'rails_helper' + +describe Gitlab::DependencyLinker::PodspecLinker, lib: true do + describe '.support?' do + it 'supports *.podspec' do + expect(described_class.support?('Reachability.podspec')).to be_truthy + end + + it 'does not support other files' do + expect(described_class.support?('.podspec.example')).to be_falsey + end + end + + describe '#link' do + let(:file_name) { "Reachability.podspec" } + + let(:file_content) do + <<-CONTENT.strip_heredoc + Pod::Spec.new do |spec| + spec.name = 'Reachability' + spec.version = '3.1.0' + spec.license = { :type => 'GPL-3.0' } + spec.license = "MIT" + spec.license = { type: 'Apache-2.0' } + spec.homepage = 'https://github.com/tonymillion/Reachability' + spec.authors = { 'Tony Million' => 'tonymillion@gmail.com' } + spec.summary = 'ARC and GCD Compatible Reachability Class for iOS and OS X.' + spec.source = { :git => 'https://github.com/tonymillion/Reachability.git', :tag => 'v3.1.0' } + spec.source_files = 'Reachability.{h,m}' + spec.framework = 'SystemConfiguration' + + spec.dependency 'AFNetworking', '~> 1.0' + spec.dependency 'RestKit/CoreData', '~> 0.20.0' + spec.ios.dependency 'MBProgressHUD', '~> 0.5' + end + CONTENT + end + + subject { Gitlab::Highlight.highlight(file_name, file_content) } + + def link(name, url) + %{#{name}} + end + + it 'links the gem name' do + expect(subject).to include(link('Reachability', 'https://cocoapods.org/pods/Reachability')) + end + + it 'links the license' do + expect(subject).to include(link('GPL-3.0', 'http://choosealicense.com/licenses/gpl-3.0/')) + expect(subject).to include(link('MIT', 'http://choosealicense.com/licenses/mit/')) + expect(subject).to include(link('Apache-2.0', 'http://choosealicense.com/licenses/apache-2.0/')) + end + + it 'links the homepage' do + expect(subject).to include(link('https://github.com/tonymillion/Reachability', 'https://github.com/tonymillion/Reachability')) + end + + it 'links the source URL' do + expect(subject).to include(link('https://github.com/tonymillion/Reachability.git', 'https://github.com/tonymillion/Reachability.git')) + end + + it 'links dependencies' do + expect(subject).to include(link('AFNetworking', 'https://cocoapods.org/pods/AFNetworking')) + expect(subject).to include(link('RestKit/CoreData', 'https://cocoapods.org/pods/RestKit')) + expect(subject).to include(link('MBProgressHUD', 'https://cocoapods.org/pods/MBProgressHUD')) + end + end +end diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb index 104e3a32e6c..b6ec756adeb 100644 --- a/spec/lib/gitlab/dependency_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker_spec.rb @@ -41,5 +41,13 @@ describe Gitlab::DependencyLinker, lib: true do described_class.link(blob_name, nil, nil) end + + it 'links using PodspecLinker' do + blob_name = 'Reachability.podspec' + + expect(described_class::PodspecLinker).to receive(:link) + + described_class.link(blob_name, nil, nil) + end end end -- cgit v1.2.1 From c35463d5f856c2f8025893f2e185825dfb691f8e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:29:34 -0500 Subject: Autolink package names in podspec.json --- lib/gitlab/dependency_linker.rb | 1 + .../dependency_linker/podspec_json_linker.rb | 32 ++++++++ .../dependency_linker/podspec_json_linker_spec.rb | 96 ++++++++++++++++++++++ spec/lib/gitlab/dependency_linker_spec.rb | 8 ++ 4 files changed, 137 insertions(+) create mode 100644 lib/gitlab/dependency_linker/podspec_json_linker.rb create mode 100644 spec/lib/gitlab/dependency_linker/podspec_json_linker_spec.rb diff --git a/lib/gitlab/dependency_linker.rb b/lib/gitlab/dependency_linker.rb index ffd6ab8e2f8..ac16b9bde2f 100644 --- a/lib/gitlab/dependency_linker.rb +++ b/lib/gitlab/dependency_linker.rb @@ -7,6 +7,7 @@ module Gitlab ComposerJsonLinker, PodfileLinker, PodspecLinker, + PodspecJsonLinker, ].freeze def self.linker(blob_name) diff --git a/lib/gitlab/dependency_linker/podspec_json_linker.rb b/lib/gitlab/dependency_linker/podspec_json_linker.rb new file mode 100644 index 00000000000..d82237ed3f1 --- /dev/null +++ b/lib/gitlab/dependency_linker/podspec_json_linker.rb @@ -0,0 +1,32 @@ +module Gitlab + module DependencyLinker + class PodspecJsonLinker < JsonLinker + include Cocoapods + + self.file_type = :podspec_json + + private + + def link_dependencies + link_json('name', json["name"], &method(:package_url)) + link_json('license', &method(:license_url)) + link_json(%w[homepage git], URL_REGEX, &:itself) + + link_packages_at_key("dependencies", &method(:package_url)) + + json["subspecs"]&.each do |subspec| + link_packages_at_key("dependencies", subspec, &method(:package_url)) + end + end + + def link_packages_at_key(key, root = json, &url_proc) + dependencies = root[key] + return unless dependencies + + dependencies.each do |name, _| + link_regex(/"(?#{Regexp.escape(name)})":\s*\[/, &url_proc) + end + end + end + end +end diff --git a/spec/lib/gitlab/dependency_linker/podspec_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/podspec_json_linker_spec.rb new file mode 100644 index 00000000000..d722865264b --- /dev/null +++ b/spec/lib/gitlab/dependency_linker/podspec_json_linker_spec.rb @@ -0,0 +1,96 @@ +require 'rails_helper' + +describe Gitlab::DependencyLinker::PodspecJsonLinker, lib: true do + describe '.support?' do + it 'supports *.podspec.json' do + expect(described_class.support?('Reachability.podspec.json')).to be_truthy + end + + it 'does not support other files' do + expect(described_class.support?('.podspec.json.example')).to be_falsey + end + end + + describe '#link' do + let(:file_name) { "AFNetworking.podspec.json" } + + let(:file_content) do + <<-CONTENT.strip_heredoc + { + "name": "AFNetworking", + "version": "2.0.0", + "license": "MIT", + "summary": "A delightful iOS and OS X networking framework.", + "homepage": "https://github.com/AFNetworking/AFNetworking", + "authors": { + "Mattt Thompson": "m@mattt.me" + }, + "source": { + "git": "https://github.com/AFNetworking/AFNetworking.git", + "tag": "2.0.0", + "submodules": true + }, + "requires_arc": true, + "platforms": { + "ios": "6.0", + "osx": "10.8" + }, + "public_header_files": "AFNetworking/*.h", + "subspecs": [ + { + "name": "NSURLConnection", + "dependencies": { + "AFNetworking/Serialization": [ + + ], + "AFNetworking/Reachability": [ + + ], + "AFNetworking/Security": [ + + ] + }, + "source_files": [ + "AFNetworking/AFURLConnectionOperation.{h,m}", + "AFNetworking/AFHTTPRequestOperation.{h,m}", + "AFNetworking/AFHTTPRequestOperationManager.{h,m}" + ] + } + ] + } + CONTENT + end + + subject { Gitlab::Highlight.highlight(file_name, file_content) } + + def link(name, url) + %{#{name}} + end + + it 'links the gem name' do + expect(subject).to include(link('AFNetworking', 'https://cocoapods.org/pods/AFNetworking')) + end + + it 'links the license' do + expect(subject).to include(link('MIT', 'http://choosealicense.com/licenses/mit/')) + end + + it 'links the homepage' do + expect(subject).to include(link('https://github.com/AFNetworking/AFNetworking', 'https://github.com/AFNetworking/AFNetworking')) + end + + it 'links the source URL' do + expect(subject).to include(link('https://github.com/AFNetworking/AFNetworking.git', 'https://github.com/AFNetworking/AFNetworking.git')) + end + + it 'links dependencies' do + expect(subject).to include(link('AFNetworking/Serialization', 'https://cocoapods.org/pods/AFNetworking')) + expect(subject).to include(link('AFNetworking/Reachability', 'https://cocoapods.org/pods/AFNetworking')) + expect(subject).to include(link('AFNetworking/Security', 'https://cocoapods.org/pods/AFNetworking')) + end + + it 'does not link subspec names' do + expect(subject).not_to include(link('NSURLConnection', 'https://cocoapods.org/pods/NSURLConnection')) + end + end +end diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb index b6ec756adeb..9fc539b604a 100644 --- a/spec/lib/gitlab/dependency_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker_spec.rb @@ -49,5 +49,13 @@ describe Gitlab::DependencyLinker, lib: true do described_class.link(blob_name, nil, nil) end + + it 'links using PodspecJsonLinker' do + blob_name = 'AFNetworking.podspec.json' + + expect(described_class::PodspecJsonLinker).to receive(:link) + + described_class.link(blob_name, nil, nil) + end end end -- cgit v1.2.1 From e780259a650ea476be0617c435798b85a252605a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:29:56 -0500 Subject: Autolink package names in Cartfile --- lib/gitlab/dependency_linker.rb | 1 + lib/gitlab/dependency_linker/cartfile_linker.rb | 20 ++++++ .../dependency_linker/cartfile_linker_spec.rb | 74 ++++++++++++++++++++++ spec/lib/gitlab/dependency_linker_spec.rb | 8 +++ 4 files changed, 103 insertions(+) create mode 100644 lib/gitlab/dependency_linker/cartfile_linker.rb create mode 100644 spec/lib/gitlab/dependency_linker/cartfile_linker_spec.rb diff --git a/lib/gitlab/dependency_linker.rb b/lib/gitlab/dependency_linker.rb index ac16b9bde2f..2f68dfc00fb 100644 --- a/lib/gitlab/dependency_linker.rb +++ b/lib/gitlab/dependency_linker.rb @@ -8,6 +8,7 @@ module Gitlab PodfileLinker, PodspecLinker, PodspecJsonLinker, + CartfileLinker, ].freeze def self.linker(blob_name) diff --git a/lib/gitlab/dependency_linker/cartfile_linker.rb b/lib/gitlab/dependency_linker/cartfile_linker.rb new file mode 100644 index 00000000000..f65577588f8 --- /dev/null +++ b/lib/gitlab/dependency_linker/cartfile_linker.rb @@ -0,0 +1,20 @@ +module Gitlab + module DependencyLinker + class CartfileLinker < MethodLinker + self.file_type = :cartfile + + private + + def link_dependencies + link_method_call(%w[github git binary]) do |value| + case value + when %r{\A#{REPO_REGEX}\z} + github_url(value) + when /\A#{URL_REGEX}\z/ + value + end + end + end + end + end +end diff --git a/spec/lib/gitlab/dependency_linker/cartfile_linker_spec.rb b/spec/lib/gitlab/dependency_linker/cartfile_linker_spec.rb new file mode 100644 index 00000000000..df77f4037af --- /dev/null +++ b/spec/lib/gitlab/dependency_linker/cartfile_linker_spec.rb @@ -0,0 +1,74 @@ +require 'rails_helper' + +describe Gitlab::DependencyLinker::CartfileLinker, lib: true do + describe '.support?' do + it 'supports Cartfile' do + expect(described_class.support?('Cartfile')).to be_truthy + end + + it 'supports Cartfile.private' do + expect(described_class.support?('Cartfile.private')).to be_truthy + end + + it 'does not support other files' do + expect(described_class.support?('test.Cartfile')).to be_falsey + end + end + + describe '#link' do + let(:file_name) { "Cartfile" } + + let(:file_content) do + <<-CONTENT.strip_heredoc + # Require version 2.3.1 or later + github "ReactiveCocoa/ReactiveCocoa" >= 2.3.1 + + # Require version 1.x + github "Mantle/Mantle" ~> 1.0 # (1.0 or later, but less than 2.0) + + # Require exactly version 0.4.1 + github "jspahrsummers/libextobjc" == 0.4.1 + + # Use the latest version + github "jspahrsummers/xcconfigs" + + # Use the branch + github "jspahrsummers/xcconfigs" "branch" + + # Use a project from GitHub Enterprise + github "https://enterprise.local/ghe/desktop/git-error-translations" + + # Use a project from any arbitrary server, on the "development" branch + git "https://enterprise.local/desktop/git-error-translations2.git" "development" + + # Use a local project + git "file:///directory/to/project" "branch" + + # A binary only framework + binary "https://my.domain.com/release/MyFramework.json" ~> 2.3 + CONTENT + end + + subject { Gitlab::Highlight.highlight(file_name, file_content) } + + def link(name, url) + %{#{name}} + end + + it 'links dependencies' do + expect(subject).to include(link('ReactiveCocoa/ReactiveCocoa', 'https://github.com/ReactiveCocoa/ReactiveCocoa')) + expect(subject).to include(link('Mantle/Mantle', 'https://github.com/Mantle/Mantle')) + expect(subject).to include(link('jspahrsummers/libextobjc', 'https://github.com/jspahrsummers/libextobjc')) + expect(subject).to include(link('jspahrsummers/xcconfigs', 'https://github.com/jspahrsummers/xcconfigs')) + end + + it 'links Git repos' do + expect(subject).to include(link('https://enterprise.local/ghe/desktop/git-error-translations', 'https://enterprise.local/ghe/desktop/git-error-translations')) + expect(subject).to include(link('https://enterprise.local/desktop/git-error-translations2.git', 'https://enterprise.local/desktop/git-error-translations2.git')) + end + + it 'links binary-only frameworks' do + expect(subject).to include(link('https://my.domain.com/release/MyFramework.json', 'https://my.domain.com/release/MyFramework.json')) + end + end +end diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb index 9fc539b604a..f27f633a62c 100644 --- a/spec/lib/gitlab/dependency_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker_spec.rb @@ -57,5 +57,13 @@ describe Gitlab::DependencyLinker, lib: true do described_class.link(blob_name, nil, nil) end + + it 'links using CartfileLinker' do + blob_name = 'Cartfile' + + expect(described_class::CartfileLinker).to receive(:link) + + described_class.link(blob_name, nil, nil) + end end end -- cgit v1.2.1 From 640440c9d8e1dea94067825e79734eb0f9774acf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:30:18 -0500 Subject: Autolink package names in Godeps.json --- lib/gitlab/dependency_linker.rb | 1 + lib/gitlab/dependency_linker/godeps_json_linker.rb | 26 +++++++ .../dependency_linker/godeps_json_linker_spec.rb | 84 ++++++++++++++++++++++ spec/lib/gitlab/dependency_linker_spec.rb | 8 +++ 4 files changed, 119 insertions(+) create mode 100644 lib/gitlab/dependency_linker/godeps_json_linker.rb create mode 100644 spec/lib/gitlab/dependency_linker/godeps_json_linker_spec.rb diff --git a/lib/gitlab/dependency_linker.rb b/lib/gitlab/dependency_linker.rb index 2f68dfc00fb..9ff355a9085 100644 --- a/lib/gitlab/dependency_linker.rb +++ b/lib/gitlab/dependency_linker.rb @@ -9,6 +9,7 @@ module Gitlab PodspecLinker, PodspecJsonLinker, CartfileLinker, + GodepsJsonLinker, ].freeze def self.linker(blob_name) diff --git a/lib/gitlab/dependency_linker/godeps_json_linker.rb b/lib/gitlab/dependency_linker/godeps_json_linker.rb new file mode 100644 index 00000000000..fe091baee6d --- /dev/null +++ b/lib/gitlab/dependency_linker/godeps_json_linker.rb @@ -0,0 +1,26 @@ +module Gitlab + module DependencyLinker + class GodepsJsonLinker < JsonLinker + NESTED_REPO_REGEX = %r{([^/]+/)+[^/]+?}.freeze + + self.file_type = :godeps_json + + private + + def link_dependencies + link_json('ImportPath') do |path| + case path + when %r{\A(?gitlab\.com/#{NESTED_REPO_REGEX})\.git/(?.+)\z}, + %r{\A(?git(lab|hub)\.com/#{REPO_REGEX})/(?.+)\z} + + "https://#{$~[:repo]}/tree/master/#{$~[:path]}" + when /\Agolang\.org/ + "https://godoc.org/#{path}" + else + "https://#{path}" + end + end + end + end + end +end diff --git a/spec/lib/gitlab/dependency_linker/godeps_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/godeps_json_linker_spec.rb new file mode 100644 index 00000000000..e279e0c9019 --- /dev/null +++ b/spec/lib/gitlab/dependency_linker/godeps_json_linker_spec.rb @@ -0,0 +1,84 @@ +require 'rails_helper' + +describe Gitlab::DependencyLinker::GodepsJsonLinker, lib: true do + describe '.support?' do + it 'supports Godeps.json' do + expect(described_class.support?('Godeps.json')).to be_truthy + end + + it 'does not support other files' do + expect(described_class.support?('Godeps.json.example')).to be_falsey + end + end + + describe '#link' do + let(:file_name) { "Godeps.json" } + + let(:file_content) do + <<-CONTENT.strip_heredoc + { + "ImportPath": "gitlab.com/gitlab-org/gitlab-pages", + "GoVersion": "go1.5", + "Packages": [ + "./..." + ], + "Deps": [ + { + "ImportPath": "github.com/kardianos/osext", + "Rev": "efacde03154693404c65e7aa7d461ac9014acd0c" + }, + { + "ImportPath": "github.com/stretchr/testify/assert", + "Rev": "1297dc01ed0a819ff634c89707081a4df43baf6b" + }, + { + "ImportPath": "github.com/stretchr/testify/require", + "Rev": "1297dc01ed0a819ff634c89707081a4df43baf6b" + }, + { + "ImportPath": "gitlab.com/group/project/path", + "Rev": "1297dc01ed0a819ff634c89707081a4df43baf6b" + }, + { + "ImportPath": "gitlab.com/group/subgroup/project.git/path", + "Rev": "1297dc01ed0a819ff634c89707081a4df43baf6b" + }, + { + "ImportPath": "golang.org/x/crypto/ssh/terminal", + "Rev": "1351f936d976c60a0a48d728281922cf63eafb8d" + }, + { + "ImportPath": "golang.org/x/net/http2", + "Rev": "b4e17d61b15679caf2335da776c614169a1b4643" + } + ] + } + CONTENT + end + + subject { Gitlab::Highlight.highlight(file_name, file_content) } + + def link(name, url) + %{#{name}} + end + + it 'links the package name' do + expect(subject).to include(link('gitlab.com/gitlab-org/gitlab-pages', 'https://gitlab.com/gitlab-org/gitlab-pages')) + end + + it 'links GitHub repos' do + expect(subject).to include(link('github.com/kardianos/osext', 'https://github.com/kardianos/osext')) + expect(subject).to include(link('github.com/stretchr/testify/assert', 'https://github.com/stretchr/testify/tree/master/assert')) + expect(subject).to include(link('github.com/stretchr/testify/require', 'https://github.com/stretchr/testify/tree/master/require')) + end + + it 'links GitLab projects' do + expect(subject).to include(link('gitlab.com/group/project/path', 'https://gitlab.com/group/project/tree/master/path')) + expect(subject).to include(link('gitlab.com/group/subgroup/project.git/path', 'https://gitlab.com/group/subgroup/project/tree/master/path')) + end + + it 'links Golang packages' do + expect(subject).to include(link('golang.org/x/net/http2', 'https://godoc.org/golang.org/x/net/http2')) + end + end +end diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb index f27f633a62c..15a8bf6a426 100644 --- a/spec/lib/gitlab/dependency_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker_spec.rb @@ -65,5 +65,13 @@ describe Gitlab::DependencyLinker, lib: true do described_class.link(blob_name, nil, nil) end + + it 'links using GodepsJsonLinker' do + blob_name = 'Godeps.json' + + expect(described_class::GodepsJsonLinker).to receive(:link) + + described_class.link(blob_name, nil, nil) + end end end -- cgit v1.2.1 From c734f2f0dd5ea5220b60a9dde9b3abef4f33fd93 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:30:29 -0500 Subject: Autolink package names in requirements.txt --- lib/gitlab/dependency_linker.rb | 1 + .../dependency_linker/requirements_txt_linker.rb | 17 +++++ .../requirements_txt_linker_spec.rb | 87 ++++++++++++++++++++++ spec/lib/gitlab/dependency_linker_spec.rb | 8 ++ 4 files changed, 113 insertions(+) create mode 100644 lib/gitlab/dependency_linker/requirements_txt_linker.rb create mode 100644 spec/lib/gitlab/dependency_linker/requirements_txt_linker_spec.rb diff --git a/lib/gitlab/dependency_linker.rb b/lib/gitlab/dependency_linker.rb index 9ff355a9085..3192bf6f667 100644 --- a/lib/gitlab/dependency_linker.rb +++ b/lib/gitlab/dependency_linker.rb @@ -10,6 +10,7 @@ module Gitlab PodspecJsonLinker, CartfileLinker, GodepsJsonLinker, + RequirementsTxtLinker ].freeze def self.linker(blob_name) diff --git a/lib/gitlab/dependency_linker/requirements_txt_linker.rb b/lib/gitlab/dependency_linker/requirements_txt_linker.rb new file mode 100644 index 00000000000..2e197e5cd94 --- /dev/null +++ b/lib/gitlab/dependency_linker/requirements_txt_linker.rb @@ -0,0 +1,17 @@ +module Gitlab + module DependencyLinker + class RequirementsTxtLinker < BaseLinker + self.file_type = :requirements_txt + + private + + def link_dependencies + link_regex(/^(?(?![a-z+]+:)[^#.-][^ ><=;\[]+)/) do |name| + "https://pypi.python.org/pypi/#{name}" + end + + link_regex(%r{^(?https?://[^ ]+)}, &:itself) + end + end + end +end diff --git a/spec/lib/gitlab/dependency_linker/requirements_txt_linker_spec.rb b/spec/lib/gitlab/dependency_linker/requirements_txt_linker_spec.rb new file mode 100644 index 00000000000..4da8821726c --- /dev/null +++ b/spec/lib/gitlab/dependency_linker/requirements_txt_linker_spec.rb @@ -0,0 +1,87 @@ +require 'rails_helper' + +describe Gitlab::DependencyLinker::RequirementsTxtLinker, lib: true do + describe '.support?' do + it 'supports requirements.txt' do + expect(described_class.support?('requirements.txt')).to be_truthy + end + + it 'supports doc-requirements.txt' do + expect(described_class.support?('doc-requirements.txt')).to be_truthy + end + + it 'does not support other files' do + expect(described_class.support?('requirements')).to be_falsey + end + end + + describe '#link' do + let(:file_name) { "requirements.txt" } + + let(:file_content) do + <<-CONTENT.strip_heredoc + # + ####### example-requirements.txt ####### + # + ###### Requirements without Version Specifiers ###### + nose + nose-cov + beautifulsoup4 + # + ###### Requirements with Version Specifiers ###### + # See https://www.python.org/dev/peps/pep-0440/#version-specifiers + docopt == 0.6.1 # Version Matching. Must be version 0.6.1 + keyring >= 4.1.1 # Minimum version 4.1.1 + coverage != 3.5 # Version Exclusion. Anything except version 3.5 + Mopidy-Dirble ~= 1.1 # Compatible release. Same as >= 1.1, == 1.* + # + ###### Refer to other requirements files ###### + -r other-requirements.txt + # + # + ###### A particular file ###### + ./downloads/numpy-1.9.2-cp34-none-win32.whl + http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl + # + ###### Additional Requirements without Version Specifiers ###### + # Same as 1st section, just here to show that you can put things in any order. + rejected + green + # + + Jinja2>=2.3 + Pygments>=1.2 + Sphinx>=1.3 + docutils>=0.7 + markupsafe + CONTENT + end + + subject { Gitlab::Highlight.highlight(file_name, file_content) } + + def link(name, url) + %{#{name}} + end + + it 'links dependencies' do + expect(subject).to include(link('nose', 'https://pypi.python.org/pypi/nose')) + expect(subject).to include(link('nose-cov', 'https://pypi.python.org/pypi/nose-cov')) + expect(subject).to include(link('beautifulsoup4', 'https://pypi.python.org/pypi/beautifulsoup4')) + expect(subject).to include(link('docopt', 'https://pypi.python.org/pypi/docopt')) + expect(subject).to include(link('keyring', 'https://pypi.python.org/pypi/keyring')) + expect(subject).to include(link('coverage', 'https://pypi.python.org/pypi/coverage')) + expect(subject).to include(link('Mopidy-Dirble', 'https://pypi.python.org/pypi/Mopidy-Dirble')) + expect(subject).to include(link('rejected', 'https://pypi.python.org/pypi/rejected')) + expect(subject).to include(link('green', 'https://pypi.python.org/pypi/green')) + expect(subject).to include(link('Jinja2', 'https://pypi.python.org/pypi/Jinja2')) + expect(subject).to include(link('Pygments', 'https://pypi.python.org/pypi/Pygments')) + expect(subject).to include(link('Sphinx', 'https://pypi.python.org/pypi/Sphinx')) + expect(subject).to include(link('docutils', 'https://pypi.python.org/pypi/docutils')) + expect(subject).to include(link('markupsafe', 'https://pypi.python.org/pypi/markupsafe')) + end + + it 'links URLs' do + expect(subject).to include(link('http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl', 'http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl')) + end + end +end diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb index 15a8bf6a426..3d1cfbcfbf7 100644 --- a/spec/lib/gitlab/dependency_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker_spec.rb @@ -73,5 +73,13 @@ describe Gitlab::DependencyLinker, lib: true do described_class.link(blob_name, nil, nil) end + + it 'links using RequirementsTxtLinker' do + blob_name = 'requirements.txt' + + expect(described_class::RequirementsTxtLinker).to receive(:link) + + described_class.link(blob_name, nil, nil) + end end end -- cgit v1.2.1 From d9f13db3a1f4e4ddfad129f1db6b462f88a347e4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 16 May 2017 15:30:44 -0500 Subject: Add changelog --- changelogs/unreleased/dm-more-dependency-linkers.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/dm-more-dependency-linkers.yml diff --git a/changelogs/unreleased/dm-more-dependency-linkers.yml b/changelogs/unreleased/dm-more-dependency-linkers.yml new file mode 100644 index 00000000000..12d45e71e85 --- /dev/null +++ b/changelogs/unreleased/dm-more-dependency-linkers.yml @@ -0,0 +1,4 @@ +--- +title: Autolink package names in more dependency files +merge_request: +author: -- cgit v1.2.1 From 4ba547809fd8fd228ea463e4f107242d1966c892 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 25 May 2017 08:43:47 -0500 Subject: Cartfile git and binary methods cannot take a GitHub repo --- lib/gitlab/dependency_linker/cartfile_linker.rb | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/gitlab/dependency_linker/cartfile_linker.rb b/lib/gitlab/dependency_linker/cartfile_linker.rb index f65577588f8..4f69f2c4ab2 100644 --- a/lib/gitlab/dependency_linker/cartfile_linker.rb +++ b/lib/gitlab/dependency_linker/cartfile_linker.rb @@ -6,14 +6,8 @@ module Gitlab private def link_dependencies - link_method_call(%w[github git binary]) do |value| - case value - when %r{\A#{REPO_REGEX}\z} - github_url(value) - when /\A#{URL_REGEX}\z/ - value - end - end + link_method_call('github', REPO_REGEX, &method(:github_url)) + link_method_call(%w[github git binary], URL_REGEX, &:itself) end end end -- cgit v1.2.1 From 41e4072393f0088c75f27d61293318844c62125e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 25 May 2017 08:55:47 -0500 Subject: Add regexp_for_value helper method --- lib/gitlab/dependency_linker/base_linker.rb | 13 +++++++++++++ lib/gitlab/dependency_linker/json_linker.rb | 27 ++++----------------------- lib/gitlab/dependency_linker/method_linker.rb | 25 ++++++++----------------- 3 files changed, 25 insertions(+), 40 deletions(-) diff --git a/lib/gitlab/dependency_linker/base_linker.rb b/lib/gitlab/dependency_linker/base_linker.rb index 15fa9f40116..7ba0b3c39b7 100644 --- a/lib/gitlab/dependency_linker/base_linker.rb +++ b/lib/gitlab/dependency_linker/base_linker.rb @@ -68,6 +68,19 @@ module Gitlab def highlighted_lines @highlighted_lines ||= highlighted_text.lines end + + def regexp_for_value(value, default: /[^'"]+/) + case value + when Array + Regexp.union(value.map { |v| regexp_for_value(v, default: default) }) + when String + Regexp.escape(value) + when Regexp + value + else + default + end + end end end end diff --git a/lib/gitlab/dependency_linker/json_linker.rb b/lib/gitlab/dependency_linker/json_linker.rb index 021347b6429..1b1ca000977 100644 --- a/lib/gitlab/dependency_linker/json_linker.rb +++ b/lib/gitlab/dependency_linker/json_linker.rb @@ -24,32 +24,13 @@ module Gitlab # link_json('specific_package', '1.0.1', link: :key) # # Will link `specific_package` in `"specific_package": "1.0.1"` def link_json(key, value = nil, link: :value, &url_proc) - key = - case key - when Array - Regexp.union(key.map { |name| Regexp.escape(name) }) - when String - Regexp.escape(key) - when nil - '[^"]+' - else - key - end - - value = - case value - when String - Regexp.escape(value) - when nil - '[^"]+' - else - value - end + key = regexp_for_value(key, default: /[^"]+/) + value = regexp_for_value(value, default: /[^"]+/) if link == :value - value = "(?#{value})" + value = /(?#{value})/ else - key = "(?#{key})" + key = /(?#{key})/ end link_regex(/"#{key}":\s*"#{value}"/, &url_proc) diff --git a/lib/gitlab/dependency_linker/method_linker.rb b/lib/gitlab/dependency_linker/method_linker.rb index eeb7c1587c0..0ffa2a83c93 100644 --- a/lib/gitlab/dependency_linker/method_linker.rb +++ b/lib/gitlab/dependency_linker/method_linker.rb @@ -20,25 +20,16 @@ module Gitlab # # link_method_call('name') # # Will link `package` in `self.name = "package"` - def link_method_call(method_names, value = nil, &url_proc) - method_names = Array(method_names).map { |name| Regexp.escape(name) } - - value = - case value - when String - Regexp.escape(value) - when nil - /[^'"]+/ - else - value - end + def link_method_call(method_name, value = nil, &url_proc) + method_name = regexp_for_value(method_name) + value = regexp_for_value(value) regex = %r{ - #{Regexp.union(method_names)} # Method name - \s* # Whitespace - [(=]? # Opening brace or equals sign - \s* # Whitespace - ['"](?#{value})['"] # Package name in quotes + #{method_name} # Method name + \s* # Whitespace + [(=]? # Opening brace or equals sign + \s* # Whitespace + ['"](?#{value})['"] # Package name in quotes }x link_regex(regex, &url_proc) -- cgit v1.2.1