octopolo 0.3.6 → 0.4.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.
- checksums.yaml +8 -8
- data/.travis.yml +4 -0
- data/CHANGELOG.markdown +5 -0
- data/lib/octopolo/commands/issue.rb +14 -0
- data/lib/octopolo/github.rb +9 -2
- data/lib/octopolo/github/issue.rb +106 -0
- data/lib/octopolo/github/issue_creator.rb +147 -0
- data/lib/octopolo/github/pull_request.rb +10 -81
- data/lib/octopolo/github/pull_request_creator.rb +12 -97
- data/lib/octopolo/renderer.rb +1 -0
- data/lib/octopolo/scripts/issue.rb +140 -0
- data/lib/octopolo/scripts/pull_request.rb +5 -71
- data/lib/octopolo/templates/issue_body.erb +22 -0
- data/lib/octopolo/version.rb +1 -1
- data/spec/octopolo/github/issue_creator_spec.rb +217 -0
- data/spec/octopolo/github/issue_spec.rb +243 -0
- data/spec/octopolo/github/pull_request_creator_spec.rb +11 -11
- data/spec/octopolo/github/pull_request_spec.rb +16 -16
- data/spec/octopolo/github_spec.rb +15 -0
- data/spec/octopolo/scripts/issue_spec.rb +233 -0
- data/spec/octopolo/scripts/pull_request_spec.rb +3 -3
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZTBjNTI0MzM3MzRiZmM4ODYxNGNlM2I4YjFjYjFiODkyZTYxMmNjNg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MjQ3MDg1YWM2ZTMyNzdiMjlhMzFkM2FlNzg0YmQyM2I5MTQ0OTNjZQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZTUzNWI1NWZkMTBiY2Y3M2MzZTg1MmEyMmIwMGEyZmJkMjg3NmJhZmE4ZmIy
|
10
|
+
MTNlNzBkYTJiMDU3NDcwYWQ5MDE1MDQ1NGE3M2NmNDYyNGY4ZGZiNDg5ZjBk
|
11
|
+
NGUzYzE4Y2NkY2FlZmU2ZGRhYjU5OTM4MWIwODA2MDBlODRhMDI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NWI5YzI3NWU5NDU2YTIxODVhNzY2OWY5OGY5MTk4M2JkMGU4YTU1OWYwZGU3
|
14
|
+
ZDk1MzIwMWZkZGYxNDBmZjhjNTczY2RkNzAwYjQ4NjQ5NTg5NmI4OWI1NThl
|
15
|
+
ZjYyNTc2YjAyMDI0MTk2ZGIwZmUwYmRkOWMwODAxMTA4ZTRkYmM=
|
data/.travis.yml
CHANGED
data/CHANGELOG.markdown
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
desc "Create an issue for the current project."
|
2
|
+
command 'issue' do |c|
|
3
|
+
config = Octopolo::Config.parse
|
4
|
+
user_config = Octopolo::UserConfig.parse
|
5
|
+
|
6
|
+
c.desc "Use $EDITOR to update PR description before creating"
|
7
|
+
c.switch [:e, :editor], :default_value => user_config.editor
|
8
|
+
|
9
|
+
c.action do |global_options, options, args|
|
10
|
+
require_relative '../scripts/issue'
|
11
|
+
options = global_options.merge(options)
|
12
|
+
Octopolo::Scripts::Issue.execute options
|
13
|
+
end
|
14
|
+
end
|
data/lib/octopolo/github.rb
CHANGED
@@ -71,6 +71,14 @@ module Octopolo
|
|
71
71
|
client.create_pull_request *args
|
72
72
|
end
|
73
73
|
|
74
|
+
def self.issue *args
|
75
|
+
client.issue *args
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.create_issue *args
|
79
|
+
client.create_issue *args
|
80
|
+
end
|
81
|
+
|
74
82
|
def self.add_comment *args
|
75
83
|
client.add_comment *args
|
76
84
|
end
|
@@ -101,12 +109,11 @@ module Octopolo
|
|
101
109
|
client.remove_label *args
|
102
110
|
end
|
103
111
|
|
104
|
-
def self.
|
112
|
+
def self.add_labels_to_issue *args
|
105
113
|
client.add_labels_to_an_issue *args
|
106
114
|
end
|
107
115
|
|
108
116
|
|
109
|
-
|
110
117
|
# now that you've set up your credentials, try again
|
111
118
|
TryAgain = Class.new(StandardError)
|
112
119
|
# the credentials you've entered are bad
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require "octokit"
|
2
|
+
|
3
|
+
module Octopolo
|
4
|
+
module GitHub
|
5
|
+
class Issue
|
6
|
+
attr_accessor :data
|
7
|
+
attr_accessor :repo_name
|
8
|
+
attr_accessor :number
|
9
|
+
|
10
|
+
def initialize repo_name, number, data = nil
|
11
|
+
raise MissingParameter if repo_name.nil? or number.nil?
|
12
|
+
|
13
|
+
self.repo_name = repo_name
|
14
|
+
self.number = number
|
15
|
+
self.data = data
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public: Create a issue for the given repo
|
19
|
+
#
|
20
|
+
# repo_name - Full name ("account/repo") of the repo in question
|
21
|
+
# options - Hash of issue information
|
22
|
+
# title: Title of the issue
|
23
|
+
# description: Brief description of the issue
|
24
|
+
#
|
25
|
+
# Returns a Issue instance
|
26
|
+
def self.create(repo_name, options)
|
27
|
+
# create via the API
|
28
|
+
creator = IssueCreator.perform(repo_name, options)
|
29
|
+
# wrap in our class
|
30
|
+
new repo_name, creator.number, creator.data
|
31
|
+
end
|
32
|
+
|
33
|
+
def data
|
34
|
+
@data ||= GitHub.issue(repo_name, number)
|
35
|
+
rescue Octokit::NotFound
|
36
|
+
raise NotFound
|
37
|
+
end
|
38
|
+
|
39
|
+
def title
|
40
|
+
data.title
|
41
|
+
end
|
42
|
+
|
43
|
+
def url
|
44
|
+
data.html_url
|
45
|
+
end
|
46
|
+
|
47
|
+
def commenter_names
|
48
|
+
exclude_octopolo_user (comments.map{ |comment| GitHub::User.new(comment.user.login).author_name }.uniq)
|
49
|
+
end
|
50
|
+
|
51
|
+
def exclude_octopolo_user(user_list)
|
52
|
+
user_list.reject{|u| GitHub.excluded_users.include?(u) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def body
|
56
|
+
data.body || ""
|
57
|
+
end
|
58
|
+
|
59
|
+
def external_urls
|
60
|
+
# extract http and https URLs from the body
|
61
|
+
URI.extract body, %w(http https)
|
62
|
+
end
|
63
|
+
|
64
|
+
def human_app_name
|
65
|
+
repo = repo_name.split("/").last
|
66
|
+
repo.split("_").map(&:capitalize).join(" ")
|
67
|
+
end
|
68
|
+
|
69
|
+
def comments
|
70
|
+
@comments ||= GitHub.issue_comments(repo_name, number)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: Add a comment to the issue
|
74
|
+
#
|
75
|
+
# message - A String containing the desired comment body
|
76
|
+
def write_comment(message)
|
77
|
+
GitHub.add_comment repo_name, number, ":octocat: #{message}"
|
78
|
+
rescue Octokit::UnprocessableEntity => error
|
79
|
+
raise CommentFailed, "Unable to write the comment: '#{error.message}'"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Public: Adds labels to a pull-request
|
83
|
+
#
|
84
|
+
# labels - label objects, can be a single label, an array of labels,
|
85
|
+
# or a list of labels
|
86
|
+
def add_labels(*labels)
|
87
|
+
built_labels = Label.build_label_array(labels)
|
88
|
+
GitHub.add_labels_to_issue(repo_name, number, built_labels.map(&:name) )
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: Removes labels from a pull-request,
|
92
|
+
#
|
93
|
+
# labels - label objects, can be a single label, an array of labels,
|
94
|
+
# or a list of labels
|
95
|
+
def remove_labels(*labels)
|
96
|
+
Label.build_label_array(labels).each do |built_label|
|
97
|
+
GitHub.remove_label(repo_name, number, built_label.name)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
MissingParameter = Class.new StandardError
|
102
|
+
NotFound = Class.new StandardError
|
103
|
+
CommentFailed = Class.new StandardError
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require_relative "../renderer"
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
module Octopolo
|
5
|
+
module GitHub
|
6
|
+
class IssueCreator
|
7
|
+
include ConfigWrapper
|
8
|
+
# for instantiating the issue creator
|
9
|
+
attr_accessor :repo_name
|
10
|
+
attr_accessor :options
|
11
|
+
# for caputuring the created issue information
|
12
|
+
attr_accessor :number
|
13
|
+
attr_accessor :data
|
14
|
+
|
15
|
+
# Public: Create a issue for the given repo with the given options
|
16
|
+
#
|
17
|
+
# repo_name - Full name ("account/repo") of the repo in question
|
18
|
+
# options - Hash of issue information
|
19
|
+
# title: Title of the issue
|
20
|
+
def initialize repo_name, options
|
21
|
+
self.repo_name = repo_name
|
22
|
+
self.options = options
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: Create a issue for the given repo with the given options
|
26
|
+
#
|
27
|
+
# repo_name - Full name ("account/repo") of the repo in question
|
28
|
+
# options - Hash of issue information
|
29
|
+
# title: Title of the issue
|
30
|
+
#
|
31
|
+
# Returns the IssueCreator instance
|
32
|
+
def self.perform repo_name, options
|
33
|
+
new(repo_name, options).tap do |creator|
|
34
|
+
creator.perform
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: Create the issue
|
39
|
+
#
|
40
|
+
# Returns an array with the first element being the issue's
|
41
|
+
# number, the second being a Mash of the response from GitHub's API
|
42
|
+
def perform
|
43
|
+
# labels option cannot be null due to https://github.com/octokit/octokit.rb/pull/538
|
44
|
+
result = GitHub.create_issue(repo_name, title, body, labels: [])
|
45
|
+
# capture the information
|
46
|
+
self.number = result.number
|
47
|
+
self.data = result
|
48
|
+
rescue => e
|
49
|
+
raise CannotCreate, e.message
|
50
|
+
end
|
51
|
+
|
52
|
+
# Public: The created resource's details
|
53
|
+
def data
|
54
|
+
@data || raise(NotYetCreated)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Public: The created issue's number
|
58
|
+
def number
|
59
|
+
@number || raise(NotYetCreated)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Public: Title of the issue
|
63
|
+
#
|
64
|
+
# Returns a String with the title
|
65
|
+
def title
|
66
|
+
options[:title] || raise(MissingAttribute)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Public: The Pivotal Tracker story IDs associated with the issue
|
70
|
+
#
|
71
|
+
# Returns an Array of Strings
|
72
|
+
def pivotal_ids
|
73
|
+
options[:pivotal_ids] || []
|
74
|
+
end
|
75
|
+
|
76
|
+
# Public: Jira Issue IDs associated with the issue
|
77
|
+
#
|
78
|
+
# Returns an Array of Strings
|
79
|
+
def jira_ids
|
80
|
+
options[:jira_ids] || []
|
81
|
+
end
|
82
|
+
|
83
|
+
# Public: Jira Url associated with the issue
|
84
|
+
#
|
85
|
+
# Returns Jira Url
|
86
|
+
def jira_url
|
87
|
+
config.jira_url
|
88
|
+
end
|
89
|
+
|
90
|
+
# Public: Rendering template for body property
|
91
|
+
#
|
92
|
+
# Returns Name of template file
|
93
|
+
def renderer_template
|
94
|
+
Renderer::ISSUE_BODY
|
95
|
+
end
|
96
|
+
|
97
|
+
# Public: Temporary file for body editing
|
98
|
+
#
|
99
|
+
# Returns Name of temporary file
|
100
|
+
def body_edit_temp_name
|
101
|
+
'octopolo_issue'
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# Public: The body (primary copy) of the issue
|
106
|
+
#
|
107
|
+
# Returns a String
|
108
|
+
def body
|
109
|
+
output = Renderer.render renderer_template, body_locals
|
110
|
+
output = edit_body(output) if options[:editor]
|
111
|
+
output
|
112
|
+
end
|
113
|
+
|
114
|
+
def edit_body(body)
|
115
|
+
return body unless ENV['EDITOR']
|
116
|
+
|
117
|
+
# Open the file, write the contents, and close it
|
118
|
+
tempfile = Tempfile.new([body_edit_temp_name, '.md'])
|
119
|
+
tempfile.write(body)
|
120
|
+
tempfile.close
|
121
|
+
|
122
|
+
# Allow the user to edit the file
|
123
|
+
system "#{ENV['EDITOR']} #{tempfile.path}"
|
124
|
+
|
125
|
+
# Reopen the file, read the contents, and delete it
|
126
|
+
tempfile.open
|
127
|
+
output = tempfile.read
|
128
|
+
tempfile.unlink
|
129
|
+
|
130
|
+
output
|
131
|
+
end
|
132
|
+
|
133
|
+
# Public: The local variables to pass into the template
|
134
|
+
def body_locals
|
135
|
+
{
|
136
|
+
pivotal_ids: pivotal_ids,
|
137
|
+
jira_ids: jira_ids,
|
138
|
+
jira_url: jira_url,
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
MissingAttribute = Class.new(StandardError)
|
143
|
+
NotYetCreated = Class.new(StandardError)
|
144
|
+
CannotCreate = Class.new(StandardError)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -1,20 +1,10 @@
|
|
1
|
+
require_relative "issue"
|
1
2
|
require_relative "../week"
|
2
3
|
require "octokit"
|
3
4
|
|
4
5
|
module Octopolo
|
5
6
|
module GitHub
|
6
|
-
class PullRequest
|
7
|
-
attr_accessor :pull_request_data
|
8
|
-
attr_accessor :repo_name
|
9
|
-
attr_accessor :number
|
10
|
-
|
11
|
-
def initialize repo_name, number, pull_request_data = nil
|
12
|
-
raise MissingParameter if repo_name.nil? or number.nil?
|
13
|
-
|
14
|
-
self.repo_name = repo_name
|
15
|
-
self.number = number
|
16
|
-
self.pull_request_data = pull_request_data
|
17
|
-
end
|
7
|
+
class PullRequest < Issue
|
18
8
|
|
19
9
|
# Public: All closed pull requests for a given repo
|
20
10
|
#
|
@@ -42,100 +32,39 @@ module Octopolo
|
|
42
32
|
# create via the API
|
43
33
|
creator = PullRequestCreator.perform(repo_name, options)
|
44
34
|
# wrap in our class
|
45
|
-
new repo_name, creator.number, creator.
|
35
|
+
new repo_name, creator.number, creator.data
|
46
36
|
end
|
47
37
|
|
48
|
-
def
|
49
|
-
@
|
38
|
+
def data
|
39
|
+
@data ||= GitHub.pull_request(repo_name, number)
|
50
40
|
rescue Octokit::NotFound
|
51
41
|
raise NotFound
|
52
42
|
end
|
53
43
|
|
54
|
-
def title
|
55
|
-
pull_request_data.title
|
56
|
-
end
|
57
|
-
|
58
|
-
def url
|
59
|
-
pull_request_data.html_url
|
60
|
-
end
|
61
|
-
|
62
44
|
def branch
|
63
|
-
|
45
|
+
data.head.ref
|
64
46
|
end
|
65
47
|
|
66
48
|
def mergeable?
|
67
|
-
|
49
|
+
data.mergeable
|
68
50
|
end
|
69
51
|
|
70
52
|
def week
|
71
|
-
Week.parse
|
53
|
+
Week.parse data.closed_at
|
72
54
|
end
|
73
55
|
|
74
56
|
def commenter_names
|
75
|
-
|
57
|
+
exclude_octopolo_user (comments.map{ |comment| GitHub::User.new(comment.user.login).author_name }.uniq - author_names)
|
76
58
|
end
|
77
59
|
|
78
60
|
def author_names
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
def exlude_octopolo_user(user_list)
|
83
|
-
user_list.reject{|u| GitHub.excluded_users.include?(u) }
|
84
|
-
end
|
85
|
-
|
86
|
-
def body
|
87
|
-
pull_request_data.body || ""
|
88
|
-
end
|
89
|
-
|
90
|
-
def external_urls
|
91
|
-
# extract http and https URLs from the body
|
92
|
-
URI.extract body, %w(http https)
|
93
|
-
end
|
94
|
-
|
95
|
-
def human_app_name
|
96
|
-
repo = repo_name.split("/").last
|
97
|
-
repo.split("_").map(&:capitalize).join(" ")
|
61
|
+
exclude_octopolo_user commits.map(&:author_name).uniq
|
98
62
|
end
|
99
63
|
|
100
64
|
def commits
|
101
65
|
@commits ||= Commit.for_pull_request self
|
102
66
|
end
|
103
67
|
|
104
|
-
def comments
|
105
|
-
@comments ||= GitHub.issue_comments(repo_name, number)
|
106
|
-
end
|
107
|
-
|
108
|
-
# Public: Add a comment to the pull request
|
109
|
-
#
|
110
|
-
# message - A String containing the desired comment body
|
111
|
-
def write_comment(message)
|
112
|
-
GitHub.add_comment repo_name, number, ":octocat: #{message}"
|
113
|
-
rescue Octokit::UnprocessableEntity => error
|
114
|
-
raise CommentFailed, "Unable to write the comment: '#{error.message}'"
|
115
|
-
end
|
116
|
-
|
117
|
-
# Public: Adds labels to a pull-request
|
118
|
-
#
|
119
|
-
# labels - label objects, can be a single label, an array of labels,
|
120
|
-
# or a list of labels
|
121
|
-
def add_labels(*labels)
|
122
|
-
built_labels = Label.build_label_array(labels)
|
123
|
-
GitHub.add_labels_to_pull(repo_name, number, built_labels.map(&:name) )
|
124
|
-
end
|
125
|
-
|
126
|
-
# Public: Removes labels from a pull-request,
|
127
|
-
#
|
128
|
-
# labels - label objects, can be a single label, an array of labels,
|
129
|
-
# or a list of labels
|
130
|
-
def remove_labels(*labels)
|
131
|
-
Label.build_label_array(labels).each do |built_label|
|
132
|
-
GitHub.remove_label(repo_name, number, built_label.name)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
MissingParameter = Class.new StandardError
|
137
|
-
NotFound = Class.new StandardError
|
138
|
-
CommentFailed = Class.new StandardError
|
139
68
|
end
|
140
69
|
end
|
141
70
|
end
|