From c6cf5313d036b085ba263df7c53bc3b2a5166a4d Mon Sep 17 00:00:00 2001 From: Stefan Exner Date: Thu, 8 Oct 2015 21:38:38 +0200 Subject: [PATCH 1/4] Comply to errbit_plugin v0.5 --- README.md | 16 +- errbit_gitlab_plugin.gemspec | 4 +- lib/errbit_gitlab_plugin.rb | 10 +- lib/errbit_gitlab_plugin/issue_tracker.rb | 190 +++++++++++++++------- lib/errbit_gitlab_plugin/version.rb | 2 +- views/gitlab_issues_body.txt.erb | 29 ---- views/gitlab_issues_summary.txt.erb | 17 -- 7 files changed, 155 insertions(+), 113 deletions(-) delete mode 100644 views/gitlab_issues_body.txt.erb delete mode 100644 views/gitlab_issues_summary.txt.erb diff --git a/README.md b/README.md index 10ba738..c03040a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # ErrbitGitlabPlugin -TODO: Write a gem description +This gem adds Gitlab issue tracker support to Errbit. + +Gem versions (currently in this fork) will equal the corresponding +`errbit_plugin` versions they are working with. ## Installation @@ -18,7 +21,16 @@ Or install it yourself as: ## Usage -TODO: Write usage instructions here +Simply add your Gitlab URL, private token and project name to your errbit app. + +You can find your private token by clicking on "Account" in your Gitlab profile settings. + +Upon saving the app, the gem will automatically test if your entered +credentials are valid and inform you otherwise. + +**Important**: The Gitlab API does not seem to accept POST requests when using +a non-encrypted connection. Therefore, please make sure to use `https://` when +using gitlab.com as URL. ## Contributing diff --git a/errbit_gitlab_plugin.gemspec b/errbit_gitlab_plugin.gemspec index 75bfbeb..fb86fe3 100644 --- a/errbit_gitlab_plugin.gemspec +++ b/errbit_gitlab_plugin.gemspec @@ -18,8 +18,8 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_runtime_dependency 'errbit_plugin', '~> 0.4', '>= 0.4.0' - spec.add_runtime_dependency 'gitlab', '~> 3.0.0', '>= 3.0.0' + spec.add_runtime_dependency 'errbit_plugin', '~> 0.5', '>= 0.5.0' + spec.add_runtime_dependency 'gitlab', '~> 3.4', '>= 3.4.0' spec.add_development_dependency 'bundler', '~> 1.3' spec.add_development_dependency 'rake', '~> 0' diff --git a/lib/errbit_gitlab_plugin.rb b/lib/errbit_gitlab_plugin.rb index dab326e..6f6a2b1 100644 --- a/lib/errbit_gitlab_plugin.rb +++ b/lib/errbit_gitlab_plugin.rb @@ -1,11 +1,11 @@ -require 'errbit_gitlab_plugin/version' -require 'errbit_gitlab_plugin/issue_tracker' -require 'errbit_gitlab_plugin/rails' - module ErrbitGitlabPlugin def self.root - File.expand_path '../..', __FILE__ + Pathname.new File.expand_path('../..', __FILE__) end end +require 'errbit_gitlab_plugin/version' +require 'errbit_gitlab_plugin/issue_tracker' +require 'errbit_gitlab_plugin/rails' + ErrbitPlugin::Registry.add_issue_tracker(ErrbitGitlabPlugin::IssueTracker) diff --git a/lib/errbit_gitlab_plugin/issue_tracker.rb b/lib/errbit_gitlab_plugin/issue_tracker.rb index 50f1346..176defd 100644 --- a/lib/errbit_gitlab_plugin/issue_tracker.rb +++ b/lib/errbit_gitlab_plugin/issue_tracker.rb @@ -4,25 +4,26 @@ module ErrbitGitlabPlugin class IssueTracker < ErrbitPlugin::IssueTracker LABEL = 'gitlab' - NOTE = '' - - FIELDS = [ - [:account, { - :label => "Gitlab URL", - :placeholder => "e.g. https://example.net" - }], - [:api_token, { - :placeholder => "API Token for your account" - }], - [:project_id, { - :label => "Ticket Project ID (use Number)", - :placeholder => "Gitlab Project where issues will be created" - }], - [:alt_project_id, { - :label => "Project Name (namespace/project)", - :placeholder => "Gitlab Project where issues will be created" - }] - ] + NOTE = "Creating issues may take some time as the actual project ID has to be looked up using the Gitlab API.
+ If you are using gitlab.com as installation, please make sure to use 'https://', otherwise, their API + will not accept some of the our commands." + + FIELDS = { + endpoint: { + label: 'Gitlab URL', + placeholder: 'The URL to your gitlab installation or the public gitlab server, e.g. https://www.gitlab.com' + }, + api_token: { + label: 'API Token', + placeholder: "Your account's API token (see Profile -> Account)" + }, + path_with_namespace: { + label: 'Project name', + placeholder: 'E.g. your_username/your_project' + } + } + + IMAGE_PATH = ErrbitGitlabPlugin.root.join('vendor', 'assets', 'images') def self.label LABEL @@ -32,66 +33,141 @@ def self.note NOTE end + # + # Form fields that will be presented to the administrator when setting up + # or editing the errbit app. The values we collect will be available for use + # later when we have an instance of this class. + # def self.fields FIELDS end - def self.body_template - @body_template ||= ERB.new(File.read( - File.join( - ErrbitGitlabPlugin.root, 'views', 'gitlab_issues_body.txt.erb' - ) - )) - end - - def self.summary_template - @summary_template ||= ERB.new(File.read( - File.join( - ErrbitGitlabPlugin.root, 'views', 'gitlab_issues_summary.txt.erb' - ) - )) + # + # Icons to display during user interactions with this issue tracker. This + # method should return a hash of two-tuples, the key names being 'create', + # 'goto', and 'inactive'. The two-tuples should contain the icon media type + # and the binary icon data. + # + def self.icons + @icons ||= { + create: ['image/png', File.read(IMAGE_PATH.join('gitlab_create.png'))], + goto: ['image/png', File.read(IMAGE_PATH.join('gitlab_goto.png'))], + inactive: ['image/png', File.read(IMAGE_PATH.join('gitlab_inactive.png'))], + } end def url - sprintf('%s/%s/issues', params['account'], params['alt_project_id']) + format '%s/%s/issues', options[:endpoint], options[:path_with_namespace] end def configured? - params['project_id'].present? && params['api_token'].present? + self.class.fields.keys.all? { |field_name| options[field_name].present? } end - def comments_allowed?; false; end + def comments_allowed? + true + end + # Called to validate user input. Just return a hash of errors if there are any def errors - errors = [] - if self.class.fields.detect {|f| params[f[0]].blank? } - errors << [:base, 'You must specify your Gitlab URL, API token, Project ID and Project Name'] + errs = [] + + # Make sure that every field is filled out + self.class.fields.except(:project_id).each_with_object({}) do |(field_name, field_options), h| + if options[field_name].blank? + errs << "#{field_options[:label]} must be present" + end + end + + # We can only perform the other tests if the necessary values are at least present + return {:base => errs.to_sentence} unless errs.size.zero? + + # Check if the given endpoint actually exists + unless gitlab_endpoint_exists?(options[:endpoint]) + errs << 'No Gitlab installation was found under the given URL' + return {:base => errs.to_sentence} end - errors + + # Check if a user by the given token exists + unless gitlab_user_exists?(options[:endpoint], options[:api_token]) + errs << 'No user with the given API token was found' + return {:base => errs.to_sentence} + end + + # Check if there is a project with the given name on the server + unless gitlab_project_id(options[:endpoint], options[:api_token], options[:path_with_namespace]) + errs << "A project named '#{options[:path_with_namespace]}' could not be found on the server" + return {:base => errs.to_sentence} + end + + {} end - def create_issue(problem, reported_by = nil) - Gitlab.configure do |config| - config.endpoint = sprintf('%s/api/v3', params['account']) - config.private_token = params['api_token'] - config.user_agent = 'Errbit User Agent' + def create_issue(title, body, reported_by = nil) + ticket = with_gitlab do |g| + g.create_issue(gitlab_project_id, title, description: body, labels: 'errbit') end - title = "[#{ problem.environment }][#{ problem.where }] #{problem.message.to_s.truncate(100)}" - description_summary = self.class.summary_template.result(binding) - description_body = self.class.body_template.result(binding) + #g.create_issue_note(gitlab_project_id, t.id, description_body) - ticket = Gitlab.create_issue(params['project_id'], title, { - :description => description_summary, - :labels => "errbit" - }) + format('%s/%s', url, ticket.id) + end - Gitlab.create_issue_note(params['project_id'], ticket.id, description_body) + private + + # + # Tries to find a project with the given name in the given Gitlab installation + # and returns its ID (if any) + # + def gitlab_project_id(gitlab_url = options[:endpoint], token = options[:api_token], project = options[:path_with_namespace]) + @project_id ||= with_gitlab(gitlab_url, token) do |g| + g.projects.detect { |p| p.path_with_namespace.downcase == project.downcase }.try(:id) + end + end + + # + # @return [String] a formatted APIv3 URL for the given +gitlab_url+ + # + def gitlab_endpoint(gitlab_url) + format '%s/api/v3', gitlab_url + end + + # + # Checks whether there is a gitlab installation + # at the given +gitlab_url+ + # + def gitlab_endpoint_exists?(gitlab_url) + with_gitlab(gitlab_url, 'Iamsecret') do |g| + g.user + end + rescue Gitlab::Error::Unauthorized + true + rescue Exception + false + end + + # + # Checks whether a user with the given +token+ exists + # in the gitlab installation located at +gitlab_url+ + # + def gitlab_user_exists?(gitlab_url, private_token) + with_gitlab(gitlab_url, private_token) do |g| + g.user + end + + true + rescue Gitlab::Error::Unauthorized + false + end - problem.update_attributes( - :issue_link => sprintf("%s/%s", url, ticket.id), - :issue_type => self.class.label - ) + # + # Connects to the gitlab installation at +gitlab_url+ + # using the given +private_token+ and executes the given block + # + def with_gitlab(gitlab_url = options[:endpoint], private_token = options[:api_token]) + yield Gitlab.client(endpoint: gitlab_endpoint(gitlab_url), + private_token: private_token, + user_agent: 'Errbit User Agent') end end end diff --git a/lib/errbit_gitlab_plugin/version.rb b/lib/errbit_gitlab_plugin/version.rb index eb04894..ea6f0eb 100644 --- a/lib/errbit_gitlab_plugin/version.rb +++ b/lib/errbit_gitlab_plugin/version.rb @@ -1,3 +1,3 @@ module ErrbitGitlabPlugin - VERSION = "0.1.0" + VERSION = '0.5.0' # Keep this version at the same value as the corresponding version of errbit_plugin end diff --git a/views/gitlab_issues_body.txt.erb b/views/gitlab_issues_body.txt.erb deleted file mode 100644 index 61d7a4b..0000000 --- a/views/gitlab_issues_body.txt.erb +++ /dev/null @@ -1,29 +0,0 @@ -<% if notice = problem.notices.first %> -## Params ## -``` -<%= pretty_hash(notice.params) %> -``` - -## Session ## -``` -<%= pretty_hash(notice.session) %> -``` - -## Backtrace ## -``` -<% notice.backtrace_lines.each do |line| %><%= line.number %>: <%= line.file_relative %> -> **<%= line.method %>** -<% end %> -``` - -## Environment ## - - -<% for key, val in notice.env_vars %> - - - - -<% end %> -
<%= key %>:<%= val %>
-<% end %> - diff --git a/views/gitlab_issues_summary.txt.erb b/views/gitlab_issues_summary.txt.erb deleted file mode 100644 index 6a7fbe9..0000000 --- a/views/gitlab_issues_summary.txt.erb +++ /dev/null @@ -1,17 +0,0 @@ -[See this exception on Errbit](<%= problem.url %> "See this exception on Errbit") -<% if notice = problem.notices.first %> -# <%= notice.message %> # -## Summary ## -<% if notice.request['url'].present? %> -### URL ### -[<%= notice.request['url'] %>](<%= notice.request['url'] %>)" -<% end %> -### Where ### -<%= notice.where %> - -### Occured ### -<%= notice.created_at.to_s(:micro) %> - -### Similar ### -<%= (notice.problem.notices_count - 1).to_s %> -<% end %> From fc44f3cba86d0968a7a2f901b41a8c5c4fa3595e Mon Sep 17 00:00:00 2001 From: Stefan Exner Date: Thu, 8 Oct 2015 22:33:02 +0200 Subject: [PATCH 2/4] Use a `read_static_file` defined in the module as seen in errbit_github_plugin --- README.md | 4 ++++ lib/errbit_gitlab_plugin.rb | 12 ++++++++---- lib/errbit_gitlab_plugin/issue_tracker.rb | 16 +++++++--------- .../assets/images => static}/gitlab_create.png | Bin .../assets/images => static}/gitlab_goto.png | Bin .../images => static}/gitlab_inactive.png | Bin 6 files changed, 19 insertions(+), 13 deletions(-) rename {vendor/assets/images => static}/gitlab_create.png (100%) rename {vendor/assets/images => static}/gitlab_goto.png (100%) rename {vendor/assets/images => static}/gitlab_inactive.png (100%) diff --git a/README.md b/README.md index c03040a..9a57d34 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,10 @@ credentials are valid and inform you otherwise. a non-encrypted connection. Therefore, please make sure to use `https://` when using gitlab.com as URL. +Also note that Gitlab's URLs are case sensitive. If the "go to issue" link is not working +for you (404), there is a high chance you provided your project name in a wrong case, +e.g. `user/my_project` instead of `User/my_project`. + ## Contributing 1. Fork it diff --git a/lib/errbit_gitlab_plugin.rb b/lib/errbit_gitlab_plugin.rb index 6f6a2b1..123fc04 100644 --- a/lib/errbit_gitlab_plugin.rb +++ b/lib/errbit_gitlab_plugin.rb @@ -1,11 +1,15 @@ +require 'errbit_gitlab_plugin/version' +require 'errbit_gitlab_plugin/issue_tracker' +require 'errbit_gitlab_plugin/rails' + module ErrbitGitlabPlugin def self.root Pathname.new File.expand_path('../..', __FILE__) end -end -require 'errbit_gitlab_plugin/version' -require 'errbit_gitlab_plugin/issue_tracker' -require 'errbit_gitlab_plugin/rails' + def self.read_static_file(file) + File.read(root.join('static', file)) + end +end ErrbitPlugin::Registry.add_issue_tracker(ErrbitGitlabPlugin::IssueTracker) diff --git a/lib/errbit_gitlab_plugin/issue_tracker.rb b/lib/errbit_gitlab_plugin/issue_tracker.rb index 176defd..a6efe7d 100644 --- a/lib/errbit_gitlab_plugin/issue_tracker.rb +++ b/lib/errbit_gitlab_plugin/issue_tracker.rb @@ -23,8 +23,6 @@ class IssueTracker < ErrbitPlugin::IssueTracker } } - IMAGE_PATH = ErrbitGitlabPlugin.root.join('vendor', 'assets', 'images') - def self.label LABEL end @@ -43,19 +41,19 @@ def self.fields end # - # Icons to display during user interactions with this issue tracker. This - # method should return a hash of two-tuples, the key names being 'create', - # 'goto', and 'inactive'. The two-tuples should contain the icon media type - # and the binary icon data. + # Icons to be displayed for this issue tracker # def self.icons @icons ||= { - create: ['image/png', File.read(IMAGE_PATH.join('gitlab_create.png'))], - goto: ['image/png', File.read(IMAGE_PATH.join('gitlab_goto.png'))], - inactive: ['image/png', File.read(IMAGE_PATH.join('gitlab_inactive.png'))], + create: ['image/png', ErrbitGitlabPlugin.read_static_file('gitlab_create.png')], + goto: ['image/png', ErrbitGitlabPlugin.read_static_file('gitlab_goto.png')], + inactive: ['image/png', ErrbitGitlabPlugin.read_static_file('gitlab_inactive.png')] } end + # + # @return [String] the URL to the given project's issues section + # def url format '%s/%s/issues', options[:endpoint], options[:path_with_namespace] end diff --git a/vendor/assets/images/gitlab_create.png b/static/gitlab_create.png similarity index 100% rename from vendor/assets/images/gitlab_create.png rename to static/gitlab_create.png diff --git a/vendor/assets/images/gitlab_goto.png b/static/gitlab_goto.png similarity index 100% rename from vendor/assets/images/gitlab_goto.png rename to static/gitlab_goto.png diff --git a/vendor/assets/images/gitlab_inactive.png b/static/gitlab_inactive.png similarity index 100% rename from vendor/assets/images/gitlab_inactive.png rename to static/gitlab_inactive.png From 0cd151c772a4d9feb94975cb9355a5035bc3550b Mon Sep 17 00:00:00 2001 From: Stefan Exner Date: Thu, 8 Oct 2015 23:26:46 +0200 Subject: [PATCH 3/4] Added issue template which looks a little nicer on gitlab --- app/views/errbit_gitlab_plugin/issue.md.erb | 43 +++++++++++++++++++++ lib/errbit_gitlab_plugin/issue_tracker.rb | 8 ++++ 2 files changed, 51 insertions(+) create mode 100644 app/views/errbit_gitlab_plugin/issue.md.erb diff --git a/app/views/errbit_gitlab_plugin/issue.md.erb b/app/views/errbit_gitlab_plugin/issue.md.erb new file mode 100644 index 0000000..3944a94 --- /dev/null +++ b/app/views/errbit_gitlab_plugin/issue.md.erb @@ -0,0 +1,43 @@ +[See this exception on Errbit](<%= app_problem_url problem.app, problem %>) + +<% if notice = problem.notices.first %> +# <%= notice.message.lines.map(&:strip).join(' ') %> + +## Summary ## +<% if notice.request['url'].present? %> +### URL ### +<%= notice.request['url'] %> +<% end %> +### Where ### + <%= notice.where %> + +### Occured ### + <%= notice.created_at.to_s(:precise) %> + +### Similar ### + <%= [notice.problem.notices_count - 1, 1].max %> + +## Params ## +```ruby +<%= pretty_hash(notice.params).html_safe %> +``` + +## Session ## +```ruby +<%= pretty_hash(notice.session).html_safe %> +``` + +## Backtrace ## +``` +<% notice.backtrace.lines.each do |line| %><%= format '%4d:', line.number %> <%= line.file_relative %> -> **<%= line.method %>** +<% end %> +``` + +<% if notice.env_vars.present? %> +## Environment ## +| Key | Value | +|------------|------------| +<% notice.env_vars.each do |key, val| %>| <%= key %> | <%= val %> | +<% end %> +<% end %> +<% end %> diff --git a/lib/errbit_gitlab_plugin/issue_tracker.rb b/lib/errbit_gitlab_plugin/issue_tracker.rb index a6efe7d..ebe7780 100644 --- a/lib/errbit_gitlab_plugin/issue_tracker.rb +++ b/lib/errbit_gitlab_plugin/issue_tracker.rb @@ -51,6 +51,14 @@ def self.icons } end + # + # Used to pass an own template to errbit's issue rendering. + # The rendered template is then passed to any #create_issue call. + # + def render_body_args + ['errbit_gitlab_plugin/issue', :formats => [:md]] + end + # # @return [String] the URL to the given project's issues section # From c490e1ebdff37a096f2a1d31707456256f270fe6 Mon Sep 17 00:00:00 2001 From: Stefan Exner Date: Fri, 9 Oct 2015 01:08:39 +0200 Subject: [PATCH 4/4] Updated README and project name validation * Validate the project name case sensitive and extend the corresponding error message * Bumped version to 1.0 due to the non-backwards-compatible changes in errbit/errbit_plugin --- README.md | 16 +++++++++------- lib/errbit_gitlab_plugin/issue_tracker.rb | 7 +++---- lib/errbit_gitlab_plugin/version.rb | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9a57d34..ca29dcd 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,10 @@ This gem adds Gitlab issue tracker support to Errbit. -Gem versions (currently in this fork) will equal the corresponding -`errbit_plugin` versions they are working with. +Version 1.0 is compatible with Errbit 0.4. + +For (much) older versions of Errbit, try the 0.x gem versions or consider +updating your Errbit installation ## Installation @@ -28,14 +30,14 @@ You can find your private token by clicking on "Account" in your Gitlab profile Upon saving the app, the gem will automatically test if your entered credentials are valid and inform you otherwise. -**Important**: The Gitlab API does not seem to accept POST requests when using +## Troubleshoot + +> I entered my Gitlab settings and the tracker was saved, but I cannot create issues + +The Gitlab API (at least on gitlab.com) does not seem to accept POST requests when using a non-encrypted connection. Therefore, please make sure to use `https://` when using gitlab.com as URL. -Also note that Gitlab's URLs are case sensitive. If the "go to issue" link is not working -for you (404), there is a high chance you provided your project name in a wrong case, -e.g. `user/my_project` instead of `User/my_project`. - ## Contributing 1. Fork it diff --git a/lib/errbit_gitlab_plugin/issue_tracker.rb b/lib/errbit_gitlab_plugin/issue_tracker.rb index ebe7780..d503eb2 100644 --- a/lib/errbit_gitlab_plugin/issue_tracker.rb +++ b/lib/errbit_gitlab_plugin/issue_tracker.rb @@ -102,7 +102,8 @@ def errors # Check if there is a project with the given name on the server unless gitlab_project_id(options[:endpoint], options[:api_token], options[:path_with_namespace]) - errs << "A project named '#{options[:path_with_namespace]}' could not be found on the server" + errs << "A project named '#{options[:path_with_namespace]}' could not be found on the server. + Please make sure to enter it exactly as it appears in your address bar in Gitlab (case sensitive)" return {:base => errs.to_sentence} end @@ -114,8 +115,6 @@ def create_issue(title, body, reported_by = nil) g.create_issue(gitlab_project_id, title, description: body, labels: 'errbit') end - #g.create_issue_note(gitlab_project_id, t.id, description_body) - format('%s/%s', url, ticket.id) end @@ -127,7 +126,7 @@ def create_issue(title, body, reported_by = nil) # def gitlab_project_id(gitlab_url = options[:endpoint], token = options[:api_token], project = options[:path_with_namespace]) @project_id ||= with_gitlab(gitlab_url, token) do |g| - g.projects.detect { |p| p.path_with_namespace.downcase == project.downcase }.try(:id) + g.projects.detect { |p| p.path_with_namespace == project }.try(:id) end end diff --git a/lib/errbit_gitlab_plugin/version.rb b/lib/errbit_gitlab_plugin/version.rb index ea6f0eb..4766800 100644 --- a/lib/errbit_gitlab_plugin/version.rb +++ b/lib/errbit_gitlab_plugin/version.rb @@ -1,3 +1,3 @@ module ErrbitGitlabPlugin - VERSION = '0.5.0' # Keep this version at the same value as the corresponding version of errbit_plugin + VERSION = '1.0.0' end