From 80ad2f3aaa6219b13972395fd30ed2889fb3692a Mon Sep 17 00:00:00 2001 From: Jeroen Jacobs Date: Mon, 24 Nov 2014 09:01:57 +0100 Subject: [PATCH] Adds GitLab formatter --- CHANGELOG.md | 1 + lib/pronto.rb | 3 + lib/pronto/formatter/formatter.rb | 1 + lib/pronto/formatter/gitlab_formatter.rb | 29 +++++++++ lib/pronto/gitlab.rb | 55 +++++++++++++++++ pronto.gemspec | 1 + spec/pronto/formatter/formatter_spec.rb | 2 +- .../pronto/formatter/gitlab_formatter_spec.rb | 61 +++++++++++++++++++ spec/pronto/gitlab_spec.rb | 31 ++++++++++ 9 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 lib/pronto/formatter/gitlab_formatter.rb create mode 100644 lib/pronto/gitlab.rb create mode 100644 spec/pronto/formatter/gitlab_formatter_spec.rb create mode 100644 spec/pronto/gitlab_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b8893fe..b4de37c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Try to detect pull request id automatically, if `PULL_REQUEST_ID` is not specified. Inspired by @willnet/prid. * [#40](https://github.com/mmozuras/pronto/issues/40): Add '--index' option for 'pronto run'. Pronto analyzes changes before committing. +* [#50](https://github.com/mmozuras/pronto/pull/50): Adds GitLab formatter ### Changes * Github and Github pull request formatters now filter out duplicate offenses on the same line to avoid spamming with redundant comments. diff --git a/lib/pronto.rb b/lib/pronto.rb index 537cba09..c7f37f62 100644 --- a/lib/pronto.rb +++ b/lib/pronto.rb @@ -1,5 +1,6 @@ require 'rugged' require 'octokit' +require 'gitlab' require 'forwardable' require 'pronto/git/repository' @@ -11,11 +12,13 @@ require 'pronto/message' require 'pronto/runner' require 'pronto/github' +require 'pronto/gitlab' require 'pronto/formatter/text_formatter' require 'pronto/formatter/json_formatter' require 'pronto/formatter/github_formatter' require 'pronto/formatter/github_pull_request_formatter' +require 'pronto/formatter/gitlab_formatter' require 'pronto/formatter/checkstyle_formatter' require 'pronto/formatter/formatter' diff --git a/lib/pronto/formatter/formatter.rb b/lib/pronto/formatter/formatter.rb index db58f831..38681490 100644 --- a/lib/pronto/formatter/formatter.rb +++ b/lib/pronto/formatter/formatter.rb @@ -12,6 +12,7 @@ def self.names FORMATTERS = { 'github' => GithubFormatter, 'github_pr' => GithubPullRequestFormatter, + 'gitlab' => GitlabFormatter, 'json' => JsonFormatter, 'checkstyle' => CheckstyleFormatter, 'text' => TextFormatter diff --git a/lib/pronto/formatter/gitlab_formatter.rb b/lib/pronto/formatter/gitlab_formatter.rb new file mode 100644 index 00000000..449529e4 --- /dev/null +++ b/lib/pronto/formatter/gitlab_formatter.rb @@ -0,0 +1,29 @@ +module Pronto + module Formatter + class GitlabFormatter + def format(messages, repo) + messages = messages.uniq { |message| [message.msg, message.line.new_lineno] } + client = Gitlab.new repo + + commit_messages = messages.map do |message| + create_comment(client, + message.commit_sha, + message.msg, + message.path, + message.line.commit_line.new_lineno) + end + + "#{commit_messages.compact.count} Pronto messages posted to GitLab" + end + + private + + def create_comment(client, sha, note, path, line) + comment = Gitlab::Comment.new(sha, note, path, line) + comments = client.commit_comments(sha) + existing = comments.any? { |c| comment == c } + client.create_commit_comment(comment) unless existing + end + end + end +end diff --git a/lib/pronto/gitlab.rb b/lib/pronto/gitlab.rb new file mode 100644 index 00000000..9584fa5a --- /dev/null +++ b/lib/pronto/gitlab.rb @@ -0,0 +1,55 @@ +module Pronto + class Gitlab + def initialize(repo) + @repo = repo + @comment_cache = {} + end + + def commit_comments(sha) + @comment_cache["#{sha}"] ||= begin + client.commit_comments(slug, sha).map do |comment| + Comment.new(sha, comment.note, comment.path, comment.line) + end + end + end + + def create_commit_comment(comment) + client.create_commit_comment(slug, comment.sha, comment.note, + path: comment.path, line: comment.line, + line_type: 'new') + end + + private + + def slug + @slug ||= begin + host = URI.split(endpoint)[2, 2].compact.join(':') + slug = @repo.remote_urls.map do |url| + match = /.*#{host}(:|\/)(?.*).git/.match(url) + match[:slug] if match + end.compact.first + URI.escape(slug, '/') if slug + end + end + + def client + @client ||= ::Gitlab.client(endpoint: endpoint, private_token: private_token) + end + + def private_token + ENV['GITLAB_API_PRIVATE_TOKEN'] + end + + def endpoint + ENV['GITLAB_API_ENDPOINT'] + end + + class Comment < Struct.new(:sha, :note, :path, :line) + def ==(other) + line == other.line && + path == other.path && + note == other.note + end + end + end +end diff --git a/pronto.gemspec b/pronto.gemspec index ec6f9ac8..dcb632a6 100644 --- a/pronto.gemspec +++ b/pronto.gemspec @@ -29,6 +29,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'rugged', '~> 0.21.0' s.add_runtime_dependency 'thor', '~> 0.19.0' s.add_runtime_dependency 'octokit', '~> 3.2' + s.add_runtime_dependency 'gitlab', '~> 3.2' s.add_development_dependency 'rake', '~> 10.3' s.add_development_dependency 'rspec', '~> 3.0' s.add_development_dependency 'rspec-its', '~> 1.0' diff --git a/spec/pronto/formatter/formatter_spec.rb b/spec/pronto/formatter/formatter_spec.rb index 2f357406..be3f52aa 100644 --- a/spec/pronto/formatter/formatter_spec.rb +++ b/spec/pronto/formatter/formatter_spec.rb @@ -43,7 +43,7 @@ module Formatter describe '.names' do subject { Formatter.names } - it { should =~ %w(github github_pr json checkstyle text) } + it { should =~ %w(github github_pr gitlab json checkstyle text) } end end end diff --git a/spec/pronto/formatter/gitlab_formatter_spec.rb b/spec/pronto/formatter/gitlab_formatter_spec.rb new file mode 100644 index 00000000..4d1e5998 --- /dev/null +++ b/spec/pronto/formatter/gitlab_formatter_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +module Pronto + module Formatter + describe GitlabFormatter do + ENV['GITLAB_API_ENDPOINT'] = 'http://example.com/api/v3' + ENV['GITLAB_API_PRIVATE_TOKEN'] = 'token' + + let(:gitlab_formatter) { GitlabFormatter.new } + + describe '#format' do + subject { gitlab_formatter.format(messages, repository) } + let(:messages) { [message, message] } + let(:repository) { Git::Repository.new('.') } + let(:message) { Message.new('path/to', line, :warning, 'crucial') } + let(:line) { double(new_lineno: 1, commit_sha: '123', position: nil) } + before { line.stub(:commit_line).and_return(line) } + + specify do + ::Gitlab::Client.any_instance + .should_receive(:commit_comments) + .once + .and_return([]) + + ::Gitlab::Client.any_instance + .should_receive(:create_commit_comment) + .once + + subject + end + end + + describe '#format without duplicates' do + subject { gitlab_formatter.format(messages, repository) } + let(:messages) { [message1, message2] } + let(:repository) { Git::Repository.new('.') } + let(:message1) { Message.new('path/to1', line1, :warning, 'crucial') } + let(:message2) { Message.new('path/to2', line2, :warning, 'crucial') } + let(:line1) { double(new_lineno: 1, commit_sha: '123', position: nil) } + let(:line2) { double(new_lineno: 2, commit_sha: '123', position: nil) } + before do + line1.stub(:commit_line).and_return(line1) + line2.stub(:commit_line).and_return(line2) + end + + specify do + ::Gitlab::Client.any_instance + .should_receive(:commit_comments) + .once + .and_return([]) + + ::Gitlab::Client.any_instance + .should_receive(:create_commit_comment) + .twice + + subject + end + end + end + end +end diff --git a/spec/pronto/gitlab_spec.rb b/spec/pronto/gitlab_spec.rb new file mode 100644 index 00000000..d013f55c --- /dev/null +++ b/spec/pronto/gitlab_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +module Pronto + describe Gitlab do + let(:gitlab) { Gitlab.new(repo) } + + describe '#commit_comments' do + subject { gitlab.commit_comments(sha) } + + context 'three requests for same comments' do + let(:repo) { double(remote_urls: ['git@gitlab.example.com:mmozuras/pronto.git']) } + let(:sha) { 'foobar' } + + specify do + ENV['GITLAB_API_ENDPOINT'] = 'http://gitlab.example.com/api/v3' + ENV['GITLAB_API_PRIVATE_TOKEN'] = 'token' + + ::Gitlab::Client.any_instance + .should_receive(:commit_comments) + .with('mmozuras%2Fpronto', sha) + .once + .and_return([]) + + subject + subject + subject + end + end + end + end +end