octopolo 0.3.6 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|