octomine 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in octomine.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Anton Minin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,27 @@
1
+ # Octomine
2
+
3
+ Redmine plugin for import issues from GitHub.
4
+
5
+ TODO: Write a gem description
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'octomine', :git => https://github.com/aminin/octomine.git
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ ## Usage
18
+
19
+ TODO: Write usage instructions here
20
+
21
+ ## Contributing
22
+
23
+ 1. Fork it
24
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
25
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
26
+ 4. Push to the branch (`git push origin my-new-feature`)
27
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,65 @@
1
+ # -*- encoding: utf-8 -*-
2
+ class ImporterController < ApplicationController
3
+ unloadable
4
+ before_filter :find_project
5
+ attr_accessor :user_map
6
+ attr_accessor :dumper
7
+
8
+ def index
9
+ if dump = fresh_dump
10
+ @user_map = {}
11
+ dump.users.each do |login|
12
+ @user_map[login] = nil
13
+ end
14
+ end
15
+ end
16
+
17
+ def import
18
+ unless dump = fresh_dump
19
+ flash[:notice] = t(:no_dump_to_import)
20
+ redirect_to :controller => :importer, :action => :index
21
+ return
22
+ end
23
+
24
+ importer = Octomine::Importer.new logger
25
+ user_map = importer.import_users YAML.load(params[:user_map]), User.current, params[:create_missing_users]
26
+
27
+ user_map.each do |k, v|
28
+ logger.info "#{k}: [#{v.id}, #{v.login}]"
29
+ end
30
+
31
+ default_tracker_id = Tracker.first.id
32
+ closed_status_id = IssueStatus.find_by_is_closed(true).id
33
+ open_status_id = IssueStatus.find_by_is_default(true).id
34
+ importer.import_issues @project.id, default_tracker_id, user_map, dump.issues, closed_status_id, open_status_id
35
+
36
+ flash[:notice] = t(:import_complete)
37
+ redirect_to :controller => :importer, :action => :index
38
+ end
39
+
40
+ def dump
41
+ login, password, repo = params[:login], params[:password], params[:repo]
42
+ dumper = Octomine::Dumper.new login, password, repo
43
+ logger.info "Dumper found #{dumper.issues.length} issues on GitHub repo #{repo}"
44
+
45
+ github_dump = File.open(Rails.root.join('tmp/github_dump'), 'w')
46
+ github_dump.print(YAML.dump(dumper))
47
+ session[:github_dump] = github_dump.path
48
+ github_dump.flush
49
+ logger.info "#{File.size(session[:github_dump])} bytes written to #{session[:github_dump]}"
50
+
51
+ flash[:notice] = t(:dump_complete)
52
+ redirect_to :controller => :importer, :action => :index
53
+ end
54
+
55
+ def fresh_dump
56
+ return nil unless session[:github_dump]
57
+ unless File.exists?(session[:github_dump])
58
+ flash[:warning] = "Dump file expired #{session[:github_dump]}"
59
+ session[:github_dump] = nil
60
+ return nil
61
+ end
62
+ return @dumper if @dumper
63
+ @dumper = YAML.load(File.read(session[:github_dump]))
64
+ end
65
+ end
@@ -0,0 +1,2 @@
1
+ module ImporterHelper
2
+ end
@@ -0,0 +1,44 @@
1
+ <h2><%=t('.issue_importer')%></h2>
2
+
3
+ <%= form_tag :action => 'dump' do %>
4
+ <%= hidden_field_tag 'project_id', @project.id %>
5
+
6
+ <fieldset class="box"><legend><%= t('.credentials') %></legend>
7
+ <p><label><%= t('.login') %></label>
8
+ <%= text_field_tag :login %></p>
9
+
10
+ <p><label><%= t('.password') %></label>
11
+ <%= text_field_tag :password, nil, :type => :password %></p>
12
+
13
+ <p><label><%= t('.repo') %></label>
14
+ <%= text_field_tag :repo %></p>
15
+
16
+ </fieldset>
17
+
18
+ <%= submit_tag t('.button_dump') %>
19
+ <% end %>
20
+
21
+ <% if @dumper && @dumper.respond_to?(:issues) %>
22
+ <div>
23
+ <h3><%= t('.dump_info') %></h3>
24
+ <dl>
25
+ <dt><%= t('.issues_dumped') %></dt><dt><%= @dumper.issues.length %></dt>
26
+ <dt><%= t('.users_dumped') %></dt><dt><%= @dumper.users.length %></dt>
27
+ </dl>
28
+ </div>
29
+ <% end %>
30
+
31
+ <%= form_tag :action => 'import' do %>
32
+ <%= hidden_field_tag 'project_id', @project.id %>
33
+
34
+ <fieldset class="box"><legend><%= t('.preferences') %></legend>
35
+ <p><label><%= t('.create_missing_users') %></label>
36
+ <%= check_box_tag :create_missing_users, '1', true %></p>
37
+
38
+ <p><label><%= t('.user_map') %></label>
39
+ <%= text_area_tag :user_map, @user_map.to_yaml %></p>
40
+
41
+ </fieldset>
42
+
43
+ <%= submit_tag t('.button_import') %>
44
+ <% end %>
@@ -0,0 +1,17 @@
1
+ # English strings go here for Rails i18n
2
+ en:
3
+ import_complete: "Import complete"
4
+ dump_complete: "Dump complete"
5
+ importer:
6
+ complete: "Complete1"
7
+ index:
8
+ issue_importer: "Import issues from GitHub"
9
+ credentials: "GitHub credentials"
10
+ login: "Login"
11
+ password: "Password"
12
+ repo: "Repo"
13
+ button_import: "Import"
14
+ button_dump: "Dump"
15
+ dump_info: "Dump Info"
16
+ issues_dumped: "issues"
17
+ users_dumped: "users"
@@ -0,0 +1,7 @@
1
+ # Plugin's routes
2
+ # See: http://guides.rubyonrails.org/routing.html
3
+ RedmineApp::Application.routes.append do
4
+ get 'projects/:id/importer', :to => 'importer#index'
5
+ post 'projects/:id/importer/import', :to => 'importer#import'
6
+ post 'projects/:id/importer/dump', :to => 'importer#dump'
7
+ end
@@ -0,0 +1,9 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Octomine
3
+
4
+ end
5
+
6
+ require 'octomine/version'
7
+ require 'octomine/importer'
8
+ require 'octomine/dumper'
9
+ require 'octomine/engine'
@@ -0,0 +1,42 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Octomine
3
+ class Dumper
4
+ attr_accessor :client
5
+ attr_accessor :issues
6
+
7
+ def initialize login, password, repo
8
+ @repo = repo
9
+ @client = Octokit::Client.new(:login => login, :password => password)
10
+ end
11
+
12
+ def issues
13
+ return @issues if @issues
14
+ save_auto_traversal = client.auto_traversal
15
+ client.auto_traversal = true
16
+ closed_issues = client.list_issues(@repo, { :state => 'closed' })
17
+ open_issues = client.list_issues(@repo)
18
+ @issues = []
19
+ @issues.concat(closed_issues).concat(open_issues)
20
+ client.auto_traversal = save_auto_traversal
21
+ @issues
22
+ end
23
+
24
+ def users
25
+ return @users if @users
26
+ @users = Set.new
27
+ issues.each do |issue|
28
+ @users.add(issue.user.login) if issue.respond_to?(:user) && issue.user
29
+ @users.add(issue.assignee.login) if issue.respond_to?(:assignee) && issue.assignee
30
+ end
31
+ @users
32
+ end
33
+
34
+ def comments
35
+
36
+ end
37
+
38
+ def issue_comments number
39
+ client.issue_commits(@repo, number)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Octomine
3
+ module Engine
4
+ class Engine < Rails::Engine
5
+ #isolate_namespace ::Neko::Analyzer
6
+ engine_name 'neko_analyzer'
7
+
8
+ # @param [Rails::Application] app
9
+ initializer 'octomine.configure_rails_initialization' do |app|
10
+ Redmine::Plugin.register :octomine do
11
+ name 'Octomine'
12
+ author 'Author name'
13
+ description 'Github importer plugin'
14
+ version Octomine::VERSION
15
+ url 'http://github.com/aminin/octomine'
16
+ author_url 'http://github.com/aminin'
17
+
18
+ permission :github_import, { :importer => [:index, :import] }, :public => true
19
+ menu :project_menu, :octomine, { :controller => 'importer', :action => 'index' }, :caption => 'GitHub Import', :last => true
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,71 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Octomine
3
+ class Importer
4
+ def initialize logger
5
+ @logger = logger
6
+ end
7
+
8
+ def import_users user_map, default_user, create_missing_users = true
9
+ user_map = user_map.map do |gh_login, rm_login|
10
+ rm_login ||= gh_login
11
+ unless user = User.find_by_login(rm_login)
12
+ if create_missing_users
13
+ logger.info "Creating missing user #{rm_login}"
14
+ user = User.new
15
+ user.login = rm_login
16
+ user.firstname = rm_login.capitalize
17
+ user.lastname = 'On GitHub'
18
+ user.mail = "#{gh_login}@example.com"
19
+ logger.warn("Errors occurred while saving user '#{rm_login}' #{user.errors.messages}") unless user.save
20
+ end
21
+ user ||= default_user
22
+ end
23
+ [gh_login, user]
24
+ end.flatten!
25
+ Hash[*user_map]
26
+ end
27
+
28
+ def import_issues project_id, tracker_id, user_map, issues, closed_status_id = nil, open_status_id = nil
29
+ logger.debug "Number of issues to import is #{issues.length}"
30
+ issues.each do |gh_issue|
31
+ logger.debug "Importing issue #{gh_issue.number} #{gh_issue.title}"
32
+ if gh_issue.pull_request && gh_issue.pull_request.empty? && gh_issue.pull_request.diff_url && !gh_issue.pull_request.diff_url.empty?
33
+ logger.debug "Skipping pull request"
34
+ logger.debug gh_issue.pull_request.inspect
35
+ next
36
+ end
37
+ rm_issue = Issue.find_by_id(gh_issue.number) || Issue.new
38
+ rm_issue.id = gh_issue.number
39
+ # Project is required
40
+ rm_issue.project_id = project_id
41
+ # Subject is required
42
+ rm_issue.subject = gh_issue.title
43
+ rm_issue.description = gh_issue.body
44
+ # Author is required
45
+ rm_issue.author_id = user_map[gh_issue.user.login].id
46
+ # Tracker is required
47
+ rm_issue.tracker_id = tracker_id
48
+
49
+ rm_issue.assigned_to_id = user_map[gh_issue.assignee.login].id if gh_issue.assignee
50
+ rm_issue.status_id = closed_status_id if gh_issue.state == 'closed'
51
+ rm_issue.status_id = open_status_id if gh_issue.state == 'open'
52
+ rm_issue.created_on = gh_issue.created_at
53
+
54
+ if rm_issue.save
55
+ logger.debug 'Issue successfully saved'
56
+ else
57
+ logger.debug "Error ocured while saving issue #{rm_issue.errors.messages}"
58
+ end
59
+
60
+ if gh_issue.comments && gh_issue.comments.to_i > 0
61
+ logger.warn 'TODO: import comments'
62
+ end
63
+ end
64
+ end
65
+
66
+ private
67
+ def logger
68
+ @logger
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Octomine
3
+ VERSION = "0.0.1"
4
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/octomine/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Anton Minin"]
6
+ gem.email = ["anton.a.minin@gmail.com"]
7
+ gem.description = %q{Redmine plugin for import issues from GitHub}
8
+ gem.summary = %q{}
9
+ gem.homepage = "https://github.com/aminin/octomine"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "octomine"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Octomine::VERSION
17
+
18
+ gem.add_dependency 'octokit'
19
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: octomine
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Anton Minin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: octokit
16
+ requirement: &9479960 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *9479960
25
+ description: Redmine plugin for import issues from GitHub
26
+ email:
27
+ - anton.a.minin@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - Gemfile
33
+ - LICENSE
34
+ - README.md
35
+ - Rakefile
36
+ - app/controllers/importer_controller.rb
37
+ - app/helpers/importer_helper.rb
38
+ - app/views/importer/index.html.erb
39
+ - config/locales/en.yml
40
+ - config/routes.rb
41
+ - lib/octomine.rb
42
+ - lib/octomine/dumper.rb
43
+ - lib/octomine/engine.rb
44
+ - lib/octomine/importer.rb
45
+ - lib/octomine/version.rb
46
+ - octomine.gemspec
47
+ homepage: https://github.com/aminin/octomine
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 1.8.11
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: ''
71
+ test_files: []
72
+ has_rdoc: