diff --git a/README.md b/README.md
index 10ba738..ca29dcd 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,11 @@
# ErrbitGitlabPlugin
-TODO: Write a gem description
+This gem adds Gitlab issue tracker support to Errbit.
+
+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
@@ -18,7 +23,20 @@ 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.
+
+## 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.
## Contributing
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/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..123fc04 100644
--- a/lib/errbit_gitlab_plugin.rb
+++ b/lib/errbit_gitlab_plugin.rb
@@ -4,7 +4,11 @@
module ErrbitGitlabPlugin
def self.root
- File.expand_path '../..', __FILE__
+ Pathname.new File.expand_path('../..', __FILE__)
+ end
+
+ def self.read_static_file(file)
+ File.read(root.join('static', file))
end
end
diff --git a/lib/errbit_gitlab_plugin/issue_tracker.rb b/lib/errbit_gitlab_plugin/issue_tracker.rb
index 50f1346..d503eb2 100644
--- a/lib/errbit_gitlab_plugin/issue_tracker.rb
+++ b/lib/errbit_gitlab_plugin/issue_tracker.rb
@@ -4,25 +4,24 @@ 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'
+ }
+ }
def self.label
LABEL
@@ -32,66 +31,148 @@ 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'
- )
- ))
+ #
+ # Icons to be displayed for this issue tracker
+ #
+ def self.icons
+ @icons ||= {
+ 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
- def self.summary_template
- @summary_template ||= ERB.new(File.read(
- File.join(
- ErrbitGitlabPlugin.root, 'views', 'gitlab_issues_summary.txt.erb'
- )
- ))
+ #
+ # 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
+ #
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
+
+ # 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.
+ Please make sure to enter it exactly as it appears in your address bar in Gitlab (case sensitive)"
+ return {:base => errs.to_sentence}
+ end
+
+ {}
+ end
+
+ 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
- errors
+
+ format('%s/%s', url, ticket.id)
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'
+ 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 == project }.try(:id)
end
+ 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)
+ #
+ # @return [String] a formatted APIv3 URL for the given +gitlab_url+
+ #
+ def gitlab_endpoint(gitlab_url)
+ format '%s/api/v3', gitlab_url
+ end
- ticket = Gitlab.create_issue(params['project_id'], title, {
- :description => description_summary,
- :labels => "errbit"
- })
+ #
+ # 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
- Gitlab.create_issue_note(params['project_id'], ticket.id, description_body)
+ #
+ # 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..4766800 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 = '1.0.0'
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
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 ##
-
-
<%= key %>: | -<%= val %> | -