github_issue_exporter 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +33 -3
- data/bin/export-github-issues +8 -6
- data/bin/import-github-issues +81 -0
- data/github_issue_exporter.gemspec +1 -1
- data/lib/issue_exporter.rb +11 -7
- data/lib/issue_exporter/cli.rb +6 -6
- data/lib/issue_exporter/directory_importer.rb +16 -0
- data/lib/issue_exporter/error_handler.rb +23 -0
- data/lib/issue_exporter/export.rb +6 -30
- data/lib/issue_exporter/github.rb +14 -0
- data/lib/issue_exporter/import.rb +87 -0
- data/lib/issue_exporter/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83363ad95196320642cfe5355440361c0ecbf873
|
4
|
+
data.tar.gz: 88c3ee537a792ea633a8a006686d47365fdf5a09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fcc17bad5c974b172e589b085f9c29fb3c705e2757e3b3bdb142c581a182f895999373f79ef259a3e30265471f1a601759e75a50c1e5b938f75521767d64a5a
|
7
|
+
data.tar.gz: 90dd4ee16a41cb77ba3adf2f234f11fab999926d38637c24ce3e3ba6f0f49184340dd40d5f6c8a1f159da991cdc5a10bd2da812e67d48ed4b08657a66255be9c
|
data/README.md
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
|
5
5
|
Need to archive some repositories that are stored on GitHub? Great. Don't forget to hold on to any open issues you may still have. GitHub Issue Exporter is a command-line utility that will download all the open issues for a single repository.
|
6
6
|
|
7
|
+
There's also another executable for importing those issues back into a repository, so you can resurrect an archived repository with all of its issues.
|
8
|
+
|
7
9
|
## Installation
|
8
10
|
|
9
11
|
GitHub Issue Exporter is built with Ruby and needs Ruby 2.0 or higher. Install it with RubyGems.
|
@@ -19,7 +21,9 @@ sudo gem install github_issue_exporter
|
|
19
21
|
```
|
20
22
|
|
21
23
|
## Usage
|
22
|
-
|
24
|
+
There are two executables, `export-github-issues` and `import-github-issues`. Both reneed at least three things to run, the owner of the repository, the repository name, and an access token that has the authority to download the Issues. You can generate this token from [here](https://github.com/settings/tokens), and clicking on "Generate new token". Here's a [blog post with some more of the details involved](http://blog.swilliams.me/words/2015/04/01/two-factor-authentication-for-github/).
|
25
|
+
|
26
|
+
### export-github-issues
|
23
27
|
|
24
28
|
The Exporter has a couple of options.
|
25
29
|
|
@@ -27,10 +31,36 @@ The Exporter has a couple of options.
|
|
27
31
|
|
28
32
|
`--output` Set the directory to store the issues in. By default it is the current directory.
|
29
33
|
|
34
|
+
The issues will be exported into either a single `issues.json` file or multiple `issue-[NUMBER].json` files.
|
35
|
+
|
36
|
+
### Example
|
37
|
+
|
38
|
+
```
|
39
|
+
export-github-issues tallwave github_issue_exporter [TOKEN]
|
40
|
+
|
41
|
+
export-github-issues --multiple-files --output ~/issues tallwave github_issue_exporter [TOKEN]
|
42
|
+
```
|
43
|
+
|
44
|
+
### import-github-issues
|
45
|
+
You can import issues a couple of different ways. First is to provide the name of each JSON file created by the exporter.
|
46
|
+
|
47
|
+
```
|
48
|
+
import-github-issues issues.json more-issues.json tallwave github_issue_exporter [TOKEN]
|
49
|
+
```
|
50
|
+
|
51
|
+
Or you can use the `--directory` flag and load all the JSON files in the specified directory.
|
52
|
+
|
53
|
+
```
|
54
|
+
import-github-issues --directory ~/issues tallwave github_issue_exporter [TOKEN]
|
55
|
+
```
|
56
|
+
|
57
|
+
#### Importer Notes
|
58
|
+
* Issues will be added to the repository as if they were brand new, so old issue numbers will not be used.
|
59
|
+
* If your user does not have push access to the repository, assignees, milestones, and labels will not be set. Read more in the [GitHub documentation](https://developer.github.com/v3/issues/#create-an-issue).
|
60
|
+
|
30
61
|
## Roadmap
|
31
62
|
|
32
|
-
*
|
33
|
-
* Allow outputting to stdout.
|
63
|
+
* Better error handling.
|
34
64
|
|
35
65
|
## Contributing
|
36
66
|
|
data/bin/export-github-issues
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby -W
|
2
2
|
# Copyright (c) 2015 Scott Williams
|
3
3
|
|
4
|
-
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) +
|
4
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
|
5
5
|
|
6
|
-
require
|
6
|
+
require "issue_exporter/cli"
|
7
7
|
|
8
8
|
module IssueExporting
|
9
9
|
class Command
|
@@ -21,8 +21,6 @@ Usage: #{$PROGRAM_NAME} [OPTION]... [OWNER] [REPO] [TOKEN]
|
|
21
21
|
|
22
22
|
Example: #{$PROGRAM_NAME} swilliams issue_exporter abcdef
|
23
23
|
|
24
|
-
TOKEN is only necessary for private repositories.
|
25
|
-
|
26
24
|
-o, --output DIRECTORY
|
27
25
|
set output path
|
28
26
|
(default: current working directory)
|
@@ -43,9 +41,13 @@ HERE
|
|
43
41
|
@multiple_files = false
|
44
42
|
end
|
45
43
|
|
44
|
+
def correct_number_of_args(arg_count)
|
45
|
+
arg_count == 3
|
46
|
+
end
|
47
|
+
|
46
48
|
def define_options(opts)
|
47
|
-
opts.on(
|
48
|
-
opts.on(
|
49
|
+
opts.on("-o", "--output ARG") { |arg| @output = arg }
|
50
|
+
opts.on("--multiple-files") { @multiple_files = true }
|
49
51
|
end
|
50
52
|
|
51
53
|
def process_input(arg, index)
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env ruby -W
|
2
|
+
# Copyright (c) 2015 Scott Williams
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
|
5
|
+
|
6
|
+
require "issue_exporter/cli"
|
7
|
+
|
8
|
+
module IssueExporting
|
9
|
+
class Command
|
10
|
+
include CLI
|
11
|
+
|
12
|
+
def about
|
13
|
+
VERSION
|
14
|
+
end
|
15
|
+
|
16
|
+
def usage
|
17
|
+
<<HERE
|
18
|
+
Import issues downloaded from an archived GitHub repository into a new one.
|
19
|
+
|
20
|
+
Usage: #{$PROGRAM_NAME} [OPTION] [FILE_NAMES]... [OWNER] [REPO] [TOKEN]
|
21
|
+
|
22
|
+
--directory DIRECTORY
|
23
|
+
rather than read individual files, read all appropriate files
|
24
|
+
in the provided directory
|
25
|
+
|
26
|
+
-h, --help display this help and exit
|
27
|
+
--version display the version
|
28
|
+
HERE
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
super
|
34
|
+
@directory = nil
|
35
|
+
@directory_to_parse = nil
|
36
|
+
@args = []
|
37
|
+
end
|
38
|
+
|
39
|
+
def correct_number_of_args(arg_count)
|
40
|
+
has_directory? ? arg_count == 1 : arg_count >= 4
|
41
|
+
end
|
42
|
+
|
43
|
+
def define_options(opts)
|
44
|
+
opts.on("--directory") { |arg| @directory = arg }
|
45
|
+
end
|
46
|
+
|
47
|
+
def perform_action
|
48
|
+
process_args
|
49
|
+
if has_directory?
|
50
|
+
importer = IssueExporting::DirectoryImporter.new @directory_to_parse
|
51
|
+
importer.import
|
52
|
+
else
|
53
|
+
importer = IssueExporting::Importer.new @files, @owner, @repo, @token
|
54
|
+
importer.import
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO: handle directory flag
|
59
|
+
def process_args
|
60
|
+
if has_directory?
|
61
|
+
@directory_to_parse = @args[0]
|
62
|
+
else
|
63
|
+
@files = @args[0...@args.count-3]
|
64
|
+
@owner = @args[@args.count-3]
|
65
|
+
@repo = @args[@args.count-2]
|
66
|
+
@token = @args[@args.count-1]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def process_input(arg, index)
|
71
|
+
@args << arg
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def has_directory?
|
76
|
+
!@directory.nil?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
IssueExporting::Command.new.run
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.email = 'scott@swilliams.me'
|
16
16
|
s.homepage = 'https://github.com/Tallwave/github_issue_exporter'
|
17
17
|
s.files = Dir['{bin,lib}/**/*'] + ['github_issue_exporter.gemspec']
|
18
|
-
s.executables = ['export-github-issues']
|
18
|
+
s.executables = ['export-github-issues', 'import-github-issues']
|
19
19
|
s.extra_rdoc_files = ['LICENSE', 'README.md']
|
20
20
|
s.require_paths = ['lib']
|
21
21
|
end
|
data/lib/issue_exporter.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
# Copyright (c) 2015 Scott Williams
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
3
|
+
require "English"
|
4
|
+
require "shellwords"
|
5
|
+
require "json"
|
6
|
+
require "issue_exporter/github"
|
7
|
+
require "issue_exporter/error_handler"
|
8
|
+
require "issue_exporter/outputter"
|
9
|
+
require "issue_exporter/export"
|
10
|
+
require "issue_exporter/import"
|
11
|
+
require "issue_exporter/directory_importer"
|
12
|
+
require "issue_exporter/error"
|
13
|
+
require "issue_exporter/version"
|
data/lib/issue_exporter/cli.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Copyright (c) 2015 Scott Williams
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "optparse"
|
4
|
+
require "issue_exporter"
|
5
5
|
|
6
6
|
module IssueExporting
|
7
7
|
module CLI
|
@@ -10,12 +10,12 @@ module IssueExporting
|
|
10
10
|
begin
|
11
11
|
OptionParser.new do |opts|
|
12
12
|
define_options opts
|
13
|
-
opts.on
|
13
|
+
opts.on "-h", "--help" do
|
14
14
|
puts usage
|
15
15
|
exit
|
16
16
|
end
|
17
17
|
|
18
|
-
opts.on
|
18
|
+
opts.on "--version" do
|
19
19
|
puts about
|
20
20
|
exit
|
21
21
|
end
|
@@ -25,8 +25,8 @@ module IssueExporting
|
|
25
25
|
raise UsageError, e
|
26
26
|
end
|
27
27
|
|
28
|
-
fail UsageError,
|
29
|
-
fail UsageError,
|
28
|
+
fail UsageError, "missing argument" if ARGV.empty?
|
29
|
+
fail UsageError, "incorrect number of arguments" unless correct_number_of_args ARGV.count
|
30
30
|
ARGV.each_with_index { |arg, index| process_input arg, index }
|
31
31
|
perform_action()
|
32
32
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module IssueExporting
|
2
|
+
class DirectoryImporter
|
3
|
+
def initialize(directory, owner, repo, token)
|
4
|
+
@directory = directory
|
5
|
+
@owner = owner
|
6
|
+
@repo = repo
|
7
|
+
@token = token
|
8
|
+
end
|
9
|
+
|
10
|
+
def import
|
11
|
+
files = Dir.glob "#{@directory}/*.json"
|
12
|
+
importer = IssueExporting::Importer.new(files, @owner, @repo, @token)
|
13
|
+
importer.import
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module IssueExporting
|
2
|
+
class ErrorHandler
|
3
|
+
def response_has_error(response)
|
4
|
+
response.code.to_i > 299
|
5
|
+
end
|
6
|
+
|
7
|
+
def error_message(response_text)
|
8
|
+
response_object = JSON.parse response_text
|
9
|
+
if response_object.is_a? Hash
|
10
|
+
response_object["message"]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle_error(error_message, should_abort = true)
|
15
|
+
msg = "ERROR: #{error_message}"
|
16
|
+
if should_abort
|
17
|
+
abort msg
|
18
|
+
else
|
19
|
+
puts msg
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Copyright (c) 2015 Scott Williams
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "net/http"
|
4
|
+
require "json"
|
5
5
|
|
6
6
|
module IssueExporting
|
7
7
|
class Exporter
|
@@ -16,40 +16,16 @@ module IssueExporting
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def export
|
19
|
-
|
19
|
+
error_handler = ErrorHandler.new
|
20
|
+
url = IssueExporting.make_uri @owner, @repo, @token
|
20
21
|
response = Net::HTTP::get url
|
21
|
-
if err = error_message(response)
|
22
|
-
handle_error err
|
22
|
+
if err = error_handler.error_message(response)
|
23
|
+
error_handler.handle_error err
|
23
24
|
else
|
24
25
|
outputter.write response
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
|
-
private
|
29
|
-
def error_message(response_text)
|
30
|
-
response_object = JSON.parse response_text
|
31
|
-
if response_object.is_a? Hash
|
32
|
-
response_object["message"]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def handle_error(error_message)
|
37
|
-
abort "ERROR: #{error_message}"
|
38
|
-
end
|
39
|
-
|
40
|
-
def make_url
|
41
|
-
url_format = @token ? url_with_token : url_without_token
|
42
|
-
url_format % [@owner, @repo, @token]
|
43
|
-
end
|
44
|
-
|
45
|
-
def url_with_token
|
46
|
-
"#{url_without_token}?access_token=%s"
|
47
|
-
end
|
48
|
-
|
49
|
-
def url_without_token
|
50
|
-
"https://api.github.com/repos/%s/%s/issues"
|
51
|
-
end
|
52
|
-
|
53
29
|
end
|
54
30
|
end
|
55
31
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module IssueExporting
|
2
|
+
def self.api_url
|
3
|
+
"https://api.github.com/repos/%s/%s/issues?access_token=%s"
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.make_url(owner, repo, token)
|
7
|
+
url_format = IssueExporting.api_url
|
8
|
+
url_format % [owner, repo, token]
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.make_uri(owner, repo, token)
|
12
|
+
URI.parse IssueExporting.make_url(owner, repo, token)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module IssueExporting
|
5
|
+
class Importer
|
6
|
+
def initialize(files, owner, repo, token)
|
7
|
+
@files = files
|
8
|
+
@owner = owner
|
9
|
+
@repo = repo
|
10
|
+
@token = token
|
11
|
+
end
|
12
|
+
|
13
|
+
def import
|
14
|
+
@files.each do |file|
|
15
|
+
file_contents = read_file file
|
16
|
+
import_json(file_contents) unless file_contents.nil?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def import_json(json_obj)
|
23
|
+
if json_obj.class == Hash
|
24
|
+
import_json_hash json_obj
|
25
|
+
elsif json_obj.class == Array
|
26
|
+
import_json_array json_obj
|
27
|
+
else
|
28
|
+
puts "Unknown object: #{json_obj.class}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def import_json_array(json_array)
|
33
|
+
json_array.each do |json_hash|
|
34
|
+
import_json_hash json_hash
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def import_json_hash(json_hash)
|
39
|
+
create_issue(filter_json json_hash)
|
40
|
+
end
|
41
|
+
|
42
|
+
def filter_json(json_hash)
|
43
|
+
json_hash.select { |k, _v| allowed_properties.include? k }
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_issue(json_obj)
|
47
|
+
uri = IssueExporting.make_uri @owner, @repo, @token
|
48
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
49
|
+
http.use_ssl = true if uri.scheme == "https"
|
50
|
+
request = Net::HTTP::Post.new uri.request_uri
|
51
|
+
request.content_type = "application/json"
|
52
|
+
request.body = json_obj.to_json
|
53
|
+
handle_response http.request(request)
|
54
|
+
end
|
55
|
+
|
56
|
+
def allowed_properties
|
57
|
+
["title", "body", "assignee", "milestone", "labels"]
|
58
|
+
end
|
59
|
+
|
60
|
+
def read_file(filename)
|
61
|
+
path = full_path filename
|
62
|
+
if File.exist? path
|
63
|
+
raw_text = File.read path
|
64
|
+
JSON.parse raw_text
|
65
|
+
else
|
66
|
+
handle_missing_file path
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def full_path(filename)
|
72
|
+
return filename if File.dirname(filename) != "."
|
73
|
+
"#{Dir.pwd}/#{filename}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def handle_response(response)
|
77
|
+
error_handler = ErrorHandler.new
|
78
|
+
if error_handler.response_has_error response
|
79
|
+
error_handler.handle_error error_handler.error_message(response.body), true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def handle_missing_file(filename)
|
84
|
+
puts "Cannot open file: #{filename}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
metadata
CHANGED
@@ -1,20 +1,21 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: github_issue_exporter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Download Issues from a GitHub repository.
|
15
15
|
email: scott@swilliams.me
|
16
16
|
executables:
|
17
17
|
- export-github-issues
|
18
|
+
- import-github-issues
|
18
19
|
extensions: []
|
19
20
|
extra_rdoc_files:
|
20
21
|
- LICENSE
|
@@ -23,11 +24,16 @@ files:
|
|
23
24
|
- LICENSE
|
24
25
|
- README.md
|
25
26
|
- bin/export-github-issues
|
27
|
+
- bin/import-github-issues
|
26
28
|
- github_issue_exporter.gemspec
|
27
29
|
- lib/issue_exporter.rb
|
28
30
|
- lib/issue_exporter/cli.rb
|
31
|
+
- lib/issue_exporter/directory_importer.rb
|
29
32
|
- lib/issue_exporter/error.rb
|
33
|
+
- lib/issue_exporter/error_handler.rb
|
30
34
|
- lib/issue_exporter/export.rb
|
35
|
+
- lib/issue_exporter/github.rb
|
36
|
+
- lib/issue_exporter/import.rb
|
31
37
|
- lib/issue_exporter/outputter.rb
|
32
38
|
- lib/issue_exporter/version.rb
|
33
39
|
homepage: https://github.com/Tallwave/github_issue_exporter
|