export-pull-requests 0.1.1 → 0.2.0

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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/Changes +23 -0
  3. data/README.md +83 -0
  4. data/bin/epr +128 -58
  5. metadata +10 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a531cf40cb67b6e178bc75a254ee821758369d6
4
- data.tar.gz: da773616d8a630e16bc43b68bf19a6dcddfca440
3
+ metadata.gz: 4d378c2d847231e7d5b29e98aeec72a952100258
4
+ data.tar.gz: f0a364c762a48ec22d0dc35ed1f94fb501bf76ca
5
5
  SHA512:
6
- metadata.gz: 859d3b348f3e95edefa52cf74de68a5a3ce253863ddd9c8695b663fa5b3488193d9672740822f51b6becf14558a9ac4d623d36f56df79fc2ddd23d365f2a3aea
7
- data.tar.gz: 883f927bd24a27b417b085550fc4697e06ce4f631c3212ce54e53e8ce74f901df02c7b33ae9d7df99f9aa20b80cdc81288deadea57c1e0eb860f8a7f7af6ca20
6
+ metadata.gz: 007731af9e311ef0f38155aa1c0a13c3dafb61211939239f3f2a5ac7483d953d1e1daac26ac1c511c51d6297595b151368c7a093918b84fab7bd84a7f879e1e6
7
+ data.tar.gz: 663950b5114a6634b51d063885fc9e862766694f4f9817b7da5f900affa39ff868629de0aab642fd4ba005febbf4bae1aceb3eba75c0923838d45ff67fc6a335
data/Changes ADDED
@@ -0,0 +1,23 @@
1
+ --------------------
2
+ v0.2.0 2017-09-21
3
+ --------------------
4
+ Enhancements:
5
+ * Add support for issues
6
+
7
+ Changes:
8
+ * Always show Repository column (was excluded if only 1 repo was given)
9
+
10
+ --------------------
11
+ v0.1.1 2017-07-23
12
+ --------------------
13
+ Enhancements:
14
+ * Add support for Bitbucket
15
+
16
+ --------------------
17
+ v0.1.0 2017-07-23
18
+ --------------------
19
+ Enhancements:
20
+ * Add support for GitLab
21
+
22
+ Changes:
23
+ * Do not require a token
@@ -0,0 +1,83 @@
1
+ # Export Pull Requests
2
+
3
+ Export pull requests/merge requests and/or issues to a CSV file.
4
+
5
+ Supports GitHub, GitLab, and Bitbucket.
6
+
7
+ ## Installation
8
+
9
+ [Ruby](https://www.ruby-lang.org/en/documentation/installation/) is required.
10
+
11
+ With Ruby installed run:
12
+
13
+ gem install export-pull-requests
14
+
15
+ This installs the `epr` executable.
16
+
17
+ ## Usage
18
+
19
+ usage: epr [-hv] [-s state] [-t token] [-c user1,user2...] user/repo1 [user/repo2...]
20
+ -c, --creator=USER1,USER2,... Export PRs created by given username(s); prepend `!' to exclude user
21
+ -h, --help Show this message
22
+ -p, --provider=NAME Service provider: bitbucket, github, or gitlab; defaults to github
23
+ -t, --token=TOKEN API token
24
+ -s, --state=STATE Export PRs in the given state, defaults to open
25
+ -v, --version epr version
26
+ -x, --export=TYPE What to export: pr, issues, or all; defaults to all
27
+
28
+ ### Config
29
+
30
+ These can all be set by one of the below methods or [via the command line](#usage).
31
+
32
+ #### Token
33
+
34
+ The API token can be set by:
35
+
36
+ * `EPR_TOKEN` environment variable
37
+ * `epr.token` setting in `.gitconfig`
38
+ * `github.oauth-token` setting in `.gitconfig`
39
+
40
+ #### Default Service
41
+
42
+ github is the default. You can set a new default via `EPR_SERVICE`.
43
+
44
+ ### Examples
45
+
46
+ Export open PRs and issues in `sshaw/git-link` and `sshaw/itunes_store_transporter`:
47
+
48
+ epr sshaw/git-link sshaw/itunes_store_transporter > pr.csv
49
+
50
+ Export open pull request not created by `sshaw` in `padrino/padrino-framework`:
51
+
52
+ epr -x pr -c '!sshaw' padrino/padrino-framework > pr.csv
53
+
54
+ Export open merge requests from a GitLab project:
55
+
56
+ epr -x pr -p gitlab gitlab-org/gitlab-ce > pr.csv
57
+
58
+ Export all issues from a GitLab project:
59
+
60
+ epr -x issues -p gitlab gitlab-org/gitlab-ce > pr.csv
61
+
62
+ ## Service Notes
63
+
64
+ ### Bitbucket
65
+
66
+ You can use [app passwords](https://confluence.atlassian.com/bitbucket/app-passwords-828781300.html) for the API token.
67
+ Just provide your token HTTP Auth style using: `username:app_password`.
68
+
69
+ ### GitLab
70
+
71
+ Authentication can be done via a [personal access token](https://gitlab.com/profile/personal_access_tokens).
72
+
73
+ Currently the API endpoint URL is hardcoded to `https://gitlab.com/api/v4`.
74
+
75
+ Enterprise editions of GitLab have an [issue export feature](https://docs.gitlab.com/ee/user/project/issues/csv_export.html).
76
+
77
+ ## Author
78
+
79
+ Skye Shaw [skye.shaw AT gmail]
80
+
81
+ ## License
82
+
83
+ Released under the MIT License: www.opensource.org/licenses/MIT
data/bin/epr CHANGED
@@ -9,8 +9,15 @@ require "github_api"
9
9
  require "gitlab"
10
10
  require "bitbucket_rest_api"
11
11
 
12
- VERSION = "0.1.1"
12
+ VERSION = "0.2.0"
13
13
  SERVICES = %w[github gitlab bitbucket]
14
+ GIT_CONFIGS = %w[epr.token github.oauth-token]
15
+
16
+ TYPE_ISSUE = "Issue"
17
+ TYPE_PR = "PR"
18
+
19
+ EXPORT_ISSUES = "issues"
20
+ EXPORT_PRS = "pr"
14
21
 
15
22
  def localtime(t)
16
23
  Time.parse(t).localtime.strftime("%x %X")
@@ -31,7 +38,7 @@ def lookup_token
31
38
  return ENV["EPR_TOKEN"] unless ENV["EPR_TOKEN"].to_s.strip.empty?
32
39
 
33
40
  begin
34
- %w[epr.token github.oauth-token].each do |setting|
41
+ GIT_CONFIGS.each do |setting|
35
42
  token = `git config #{setting}`.chomp
36
43
  return token unless token.empty?
37
44
  end
@@ -41,33 +48,78 @@ def lookup_token
41
48
  end
42
49
 
43
50
  def bitbucket(user, repo)
44
- page = 0
45
- rows = []
46
-
47
- $bitbucket ||= BitBucket.new(:basic_auth => $token)
48
51
  # TODO: make sure no need to translate any states
49
52
  # https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/pullrequests
50
53
 
51
- loop do
52
- page += 1
54
+ $bitbucket ||= BitBucket.new(:basic_auth => $token)
53
55
 
54
- prs = $bitbucket.repos.pull_request.all(user, repo, :page => page, :state => $filter.upcase)
55
- prs["values"].each do |pr|
56
- next if pr.author && skip_user?(pr.author.username)
56
+ rows = []
57
+ no_user = "Anonymous"
58
+ repo_name = "#{user}/#{repo}"
59
+
60
+ pull_requests = lambda do
61
+ page = 0
62
+
63
+ loop do
64
+ page += 1
65
+
66
+ prs = $bitbucket.repos.pull_request.all(user, repo, :page => page, :state => $filter.upcase)
67
+ prs["values"].each do |pr|
68
+ next if pr.author && skip_user?(pr.author.username)
69
+
70
+ rows << [
71
+ repo_name,
72
+ TYPE_PR,
73
+ pr.id,
74
+ pr.author ? pr.author.username : no_user,
75
+ pr.title,
76
+ pr.state,
77
+ localtime(pr.created_on),
78
+ localtime(pr.updated_on),
79
+ pr["links"].html.href
80
+ ]
81
+ end
82
+
83
+ break unless prs["next"]
84
+ end
85
+ end
57
86
 
58
- rows << [
59
- "#{user}/#{repo}",
60
- pr.id,
61
- pr.author ? pr.author.username : "-",
62
- pr.title,
63
- pr.state,
64
- localtime(pr.created_on),
65
- localtime(pr.updated_on),
66
- pr["links"].html.href
67
- ]
87
+ issues = lambda do
88
+ start = 0
89
+
90
+ loop do
91
+ issues = $bitbucket.issues.list_repo(user, repo, :start => start, :status => $filter)
92
+ break unless issues.any?
93
+
94
+ issues.each do |issue|
95
+ next if issue["reported_by"] && skip_user?(issue["reported_by"]["username"])
96
+
97
+ rows << [
98
+ repo_name,
99
+ TYPE_ISSUE,
100
+ issue["local_id"],
101
+ issue["reported_by"] ? issue["reported_by"]["username"] : no_user,
102
+ issue["title"],
103
+ issue["status"],
104
+ localtime(issue["utc_created_on"]),
105
+ localtime(issue["utc_last_updated"]),
106
+ # Not in response
107
+ sprintf("https://bitbucket.org/%s/issues/%s", repo_name, issue["local_id"])
108
+ ]
109
+ end
110
+
111
+ start += issues.size
68
112
  end
113
+ end
69
114
 
70
- break unless prs["next"]
115
+ case $export
116
+ when EXPORT_PRS
117
+ pull_requests[]
118
+ when EXPORT_ISSUES
119
+ issues[]
120
+ else
121
+ pull_requests[]
122
+ issues[]
71
123
  end
72
124
 
73
125
  rows
@@ -75,23 +127,27 @@ end
75
127
 
76
128
  def github(user, repo)
77
129
  rows = []
130
+ method = $export == EXPORT_PRS ? :pull_requests : :issues
78
131
 
79
132
  $gh ||= Github.new(:oauth_token => $token, :auto_pagination => true)
80
- $gh.pull_requests.list(:user => user, :repo => repo, :state => $filter).each_page do |page|
133
+ $gh.public_send(method).list(:user => user, :repo => repo, :state => $filter).each_page do |page|
81
134
  next if page.size.zero? # Needed for auto_pagination
82
135
 
83
- page.each do |pr|
84
- next if skip_user?(pr.user.login)
136
+ page.each do |item|
137
+ # issues method will return issues and PRs
138
+ next if $export == EXPORT_ISSUES && item.pull_request
139
+ next if skip_user?(item.user.login)
85
140
 
86
141
  rows << [
87
142
  "#{user}/#{repo}",
88
- pr.number,
89
- pr.user.login,
90
- pr.title,
91
- pr.state,
92
- localtime(pr.created_at),
93
- localtime(pr.updated_at),
94
- pr.html_url,
143
+ item.pull_request ? TYPE_PR : TYPE_ISSUE,
144
+ item.number,
145
+ item.user.login,
146
+ item.title,
147
+ item.state,
148
+ localtime(item.created_at),
149
+ localtime(item.updated_at),
150
+ item.html_url,
95
151
  ]
96
152
  end
97
153
  end
@@ -102,34 +158,47 @@ end
102
158
  def gitlab(user, repo)
103
159
  rows = []
104
160
 
161
+ case $export
162
+ when EXPORT_PRS
163
+ methods = [:merge_requests]
164
+ when EXPORT_ISSUES
165
+ methods = [:issues]
166
+ else
167
+ methods = [:merge_requests, :issues]
168
+ end
169
+
105
170
  # Do we care about this differing in output?
106
171
  state = $filter == "open" ? "opened" : $filter
107
172
 
108
173
  # TODO: custom endpoint
109
- $gitlab ||= Gitlab.client(:token => $token, :endpoint => "https://gitlab.com/api/v4")
110
- $gitlab.merge_requests("#{user}/#{repo}", :state => state).auto_paginate do |mr|
111
- next if skip_user?(mr.author.username)
112
-
113
- rows << [
114
- "#{user}/#{repo}",
115
- # Yes, it's called iid
116
- mr.iid,
117
- mr.author.username,
118
- mr.title,
119
- mr.state,
120
- localtime(mr.created_at),
121
- localtime(mr.updated_at),
122
- mr.web_url
123
- ]
174
+ $gitlab ||= Gitlab.client(:auth_token => $token, :endpoint => "https://gitlab.com/api/v4")
175
+ methods.each do |method|
176
+ $gitlab.public_send(method, "#{user}/#{repo}", :state => state).auto_paginate do |item|
177
+ next if skip_user?(item.author.username)
178
+
179
+ rows << [
180
+ "#{user}/#{repo}",
181
+ method == :issues ? TYPE_ISSUE : TYPE_PR,
182
+ # Yes, it's called iid
183
+ item.iid,
184
+ item.author.username,
185
+ item.title,
186
+ item.state,
187
+ localtime(item.created_at),
188
+ localtime(item.updated_at),
189
+ item.web_url
190
+ ]
191
+ end
124
192
  end
125
193
 
126
194
  rows
127
195
  end
128
196
 
129
- def export_repos(repos)
197
+ def export_repos(argv)
130
198
  rows = []
131
- rows << %w[Repository # User Title State Created Updated URL]
199
+ rows << %w[Repository Type # User Title State Created Updated URL]
132
200
 
201
+ repos = parse_repos(argv)
133
202
  repos.each do |user, repo|
134
203
  case $provider
135
204
  when "github"
@@ -142,11 +211,7 @@ def export_repos(repos)
142
211
  abort "unknown service provider: #$provider"
143
212
  end
144
213
 
145
- rows.each do |r|
146
- # Drop Repository column if we only have one repo
147
- r.shift unless repos.size > 1
148
- puts r.to_csv
149
- end
214
+ rows.each { |r| puts r.to_csv }
150
215
  rows.clear
151
216
  end
152
217
  end
@@ -157,6 +222,7 @@ Hashie.logger = Logger.new(File::NULL) if defined?(Hashie)
157
222
 
158
223
  $exclude_users = []
159
224
  $include_users = []
225
+ $export = "all"
160
226
  $filter = "open"
161
227
  $provider = ENV["EPR_SERVICE"] || SERVICES[0]
162
228
  $token = lookup_token
@@ -164,7 +230,7 @@ $token = lookup_token
164
230
  parser = OptionParser.new do |opts|
165
231
  opts.banner = "usage: #{File.basename($0)} [-hv] [-s state] [-t token] [-c user1,user2...] user/repo1 [user/repo2...]"
166
232
 
167
- opts.on "-c", "--creator=user1,user2,...", Array, "Export PRs created by given username(s); prepend `!' to exclude user" do |u|
233
+ opts.on "-c", "--creator=USER1,USER2,...", Array, "Export PRs created by given username(s); prepend `!' to exclude user" do |u|
168
234
  $exclude_users, $include_users = u.partition { |name| name.start_with?("!") }
169
235
  $exclude_users.map! { |name| name[1..-1] } # remove "!"
170
236
  end
@@ -178,14 +244,18 @@ parser = OptionParser.new do |opts|
178
244
  $token = t
179
245
  end
180
246
 
181
- opts.on "-p, --provider=NAME", SERVICES, "Service provider, one of: #{SERVICES}" do |name|
247
+ opts.on "-p, --provider=NAME", SERVICES, "Service provider: bitbucket, github, or gitlab; defaults to github" do |name|
182
248
  $provider = name
183
249
  end
184
250
 
185
- opts.on "-s", "--state=STATE", %w[open closed all merged], "Export PRs in the given state, defaults to open" do |f|
251
+ opts.on "-s", "--state=STATE", "Export items in the given state, defaults to open" do |f|
186
252
  $filter = f
187
253
  end
188
254
 
255
+ opts.on "-x", "--export=WHAT", %w[pr issues all], "What to export: pr, issues, or all; defaults to all" do |x|
256
+ $export = x
257
+ end
258
+
189
259
  opts.on "-v", "--version", "epr version" do
190
260
  puts "v#{VERSION} (GitHub v#{Github::VERSION}, GitLab v#{Gitlab::VERSION}, Bitbucket v#{BitBucket::VERSION::STRING})"
191
261
  exit
@@ -196,7 +266,7 @@ parser.parse!
196
266
  abort parser.banner if ARGV.empty?
197
267
 
198
268
  begin
199
- export_repos(parse_repos(ARGV))
269
+ export_repos(ARGV)
200
270
  rescue => e
201
271
  abort "Export failed: #{e}"
202
272
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: export-pull-requests
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Skye Shaw
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-23 00:00:00.000000000 Z
11
+ date: 2017-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: github_api
@@ -67,19 +67,23 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.9'
69
69
  description: Program to export GitHub, GitLab, or Bitbucket pull requests/merge requests
70
- to CSV a file.
70
+ and issues to CSV a file.
71
71
  email: skye.shaw@gmail.com
72
72
  executables:
73
73
  - epr
74
74
  extensions: []
75
- extra_rdoc_files: []
75
+ extra_rdoc_files:
76
+ - README.md
77
+ - Changes
76
78
  files:
79
+ - Changes
80
+ - README.md
77
81
  - bin/epr
78
82
  homepage: https://github.com/sshaw/export-pull-requests
79
83
  licenses:
80
84
  - MIT
81
85
  metadata: {}
82
- post_install_message:
86
+ post_install_message: Use the `epr' command to export your pull requests.
83
87
  rdoc_options: []
84
88
  require_paths:
85
89
  - lib
@@ -98,5 +102,5 @@ rubyforge_project:
98
102
  rubygems_version: 2.4.5.1
99
103
  signing_key:
100
104
  specification_version: 4
101
- summary: Export pull requests to a CSV file.
105
+ summary: Export pull requests and issues to a CSV file.
102
106
  test_files: []