lita-github 0.0.13 → 0.0.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/lita-github.rb +1 -0
- data/lib/lita-github/general.rb +23 -17
- data/lib/lita-github/r.rb +2 -6
- data/lib/lita-github/version.rb +2 -2
- data/lib/lita/handlers/github.rb +6 -3
- data/lib/lita/handlers/github_issues.rb +110 -0
- data/lib/lita/handlers/github_org.rb +132 -1
- data/lib/lita/handlers/github_pr.rb +1 -1
- data/locales/en.yml +26 -2
- data/spec/unit/lita-github/general_spec.rb +27 -12
- data/spec/unit/lita-github/r_spec.rb +0 -44
- data/spec/unit/lita-github/version_spec.rb +7 -7
- data/spec/unit/lita/handlers/github_issues_spec.rb +162 -0
- data/spec/unit/lita/handlers/github_org_spec.rb +259 -2
- data/spec/unit/lita/handlers/github_pr_spec.rb +63 -49
- data/spec/unit/lita/handlers/github_repo_spec.rb +1 -1
- data/spec/unit/lita/handlers/github_spec.rb +20 -8
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2012ebdeed7b0904f7d65cb1d1da4fe0988a031b
|
4
|
+
data.tar.gz: f8f82d62bd1e38ab05156f65b8ec159ae265b98b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c74417471b025d45253f622a97e054e1cfa49a0fabbf55879c9b14b45d9293b414407e62c1d357d40a16a66817616831143d725665e9a720ba8472775d8dae5
|
7
|
+
data.tar.gz: bfba8e8cb4ea94d8b6164c5489bcb3d4624be502f28b2a8a1a357ce6289f7b3559da6567647685ac6b592dec74d1a73d505cb4002b1313716a3100e27f78afe3
|
data/lib/lita-github.rb
CHANGED
data/lib/lita-github/general.rb
CHANGED
@@ -21,31 +21,37 @@ module LitaGithub
|
|
21
21
|
#
|
22
22
|
# @author Tim Heckman <tim@pagerduty.com>
|
23
23
|
module General
|
24
|
-
#
|
24
|
+
# Convert the value of parameter 1 to an Integer if it looks like it should be one
|
25
25
|
#
|
26
26
|
# @author Tim Heckman <tim@pagerduty.com>
|
27
|
-
# @param
|
28
|
-
# @return [
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
k, v = opt.strip.split(':')
|
33
|
-
k = k.to_sym
|
34
|
-
o[k] = v unless o.key?(k)
|
35
|
-
end
|
36
|
-
o
|
27
|
+
# @param value [String] the value to see if it should be an Integer
|
28
|
+
# @return [Integer] if the String <tt>value</tt> looks like an Integer (<tt>/^\d+$/</tt>), return it as one
|
29
|
+
# @return [String] if the String <tt>value</tt> is not an Integer, return as is
|
30
|
+
def to_i_if_numeric(value)
|
31
|
+
/^\d+$/.match(value) ? value.to_i : value
|
37
32
|
end
|
38
33
|
|
39
|
-
#
|
34
|
+
# For use when parsing options using <tt>opt_parse()</tt>, this method converts the key to a downcased symbol
|
35
|
+
# for use within the option Hash.
|
36
|
+
#
|
37
|
+
# @author Tim Heckman <tim@pagerduty.com>
|
38
|
+
# @param k [String] the key
|
39
|
+
# @param v [Any] the value
|
40
|
+
# @return [Array] the first element is <tt>key</tt> as a downcased symbol, the second is <tt>v</tt> with no changes
|
41
|
+
def symbolize_opt_key(k, v)
|
42
|
+
[k.downcase.to_sym, v]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Parse the options in the command using the option regex
|
40
46
|
#
|
41
47
|
# @author Tim Heckman <tim@pagerduty.com>
|
42
48
|
# @param cmd [String] the full command line provided to Lita
|
43
49
|
# @return [Hash] the key:value pairs that were in the command string
|
44
|
-
def
|
50
|
+
def opts_parse(cmd)
|
45
51
|
o = {}
|
46
|
-
cmd.scan(LitaGithub::R::
|
47
|
-
k, v = opt.strip.split(':')
|
48
|
-
|
52
|
+
cmd.scan(LitaGithub::R::OPT_REGEX).flatten.each do |opt|
|
53
|
+
k, v = symbolize_opt_key(*opt.strip.split(':'))
|
54
|
+
next if o.key?(k)
|
49
55
|
|
50
56
|
# if it looks like we're using the extended option (first character is a " or '):
|
51
57
|
# slice off the first and last character of the string
|
@@ -53,7 +59,7 @@ module LitaGithub
|
|
53
59
|
# do nothing
|
54
60
|
v = v.slice!(1, (v.length - 2)) if %w(' ").include?(v.slice(0))
|
55
61
|
|
56
|
-
o[k] = v
|
62
|
+
o[k] = to_i_if_numeric(v)
|
57
63
|
end
|
58
64
|
o
|
59
65
|
end
|
data/lib/lita-github/r.rb
CHANGED
@@ -22,13 +22,9 @@ module LitaGithub
|
|
22
22
|
# command alias regex (!gh or !github for example)
|
23
23
|
A_REG ||= '(?:gh|github)\s+?'
|
24
24
|
|
25
|
-
# option regex, letting you scan the
|
26
|
-
# key1:value key2:value, etc.
|
27
|
-
OPT_REGEX ||= /((?:\s+?[a-zA-Z0-9_]+?):(?:[a-zA-Z0-9_]+))/
|
28
|
-
|
29
|
-
# extended option regex, letting you scan the string for things like:
|
25
|
+
# option regex, letting you scan the string for things like:
|
30
26
|
# key1:value key2:"value2" key3:'a better value'
|
31
|
-
|
27
|
+
OPT_REGEX ||= /((?:\s+?[a-zA-Z0-9_]+?):(?:(?:".+?")|(?:'.+?')|(?:[a-zA-Z0-9_]+)))/
|
32
28
|
|
33
29
|
# regex matcher for: Org/repo
|
34
30
|
REPO_REGEX = '(?:(?<org>[a-zA-Z0-9_\-]+)(?:\s+?)?\/)?(?:\s+?)?(?<repo>[a-zA-Z0-9_\-]+)'
|
data/lib/lita-github/version.rb
CHANGED
@@ -21,8 +21,8 @@
|
|
21
21
|
# @author Tim Heckman <tim@pagerduty.com>
|
22
22
|
module LitaGithub
|
23
23
|
# lita-github version
|
24
|
-
VERSION = '0.0.
|
24
|
+
VERSION = '0.0.14'
|
25
25
|
|
26
26
|
# lita-github version split amongst different revisions
|
27
|
-
|
27
|
+
MAJOR_VERSION, MINOR_VERSION, REVISION = VERSION.split('.').map(&:to_i)
|
28
28
|
end
|
data/lib/lita/handlers/github.rb
CHANGED
@@ -68,8 +68,9 @@ module Lita
|
|
68
68
|
def self.default_config(config)
|
69
69
|
# when setting default configuration values please remember one thing:
|
70
70
|
# secure and safe by default
|
71
|
-
config.default_team_slug
|
72
|
-
config.repo_private_default
|
71
|
+
config.default_team_slug = nil
|
72
|
+
config.repo_private_default = true
|
73
|
+
config.org_team_add_allowed_perms = %w(pull)
|
73
74
|
|
74
75
|
####
|
75
76
|
# Method Filters
|
@@ -92,7 +93,9 @@ module Lita
|
|
92
93
|
# Lita::Handlers::GithubOrg
|
93
94
|
config.org_team_add_enabled = false
|
94
95
|
config.org_team_rm_enabled = false
|
95
|
-
config.
|
96
|
+
config.org_user_add_enabled = false
|
97
|
+
config.org_user_rm_enabled = false
|
98
|
+
config.org_eject_user_enabled = false
|
96
99
|
end
|
97
100
|
|
98
101
|
def status(response)
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright 2014 PagerDuty, Inc.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'lita-github/r'
|
18
|
+
require 'lita-github/general'
|
19
|
+
require 'lita-github/config'
|
20
|
+
require 'lita-github/octo'
|
21
|
+
require 'lita-github/org'
|
22
|
+
require 'lita-github/repo'
|
23
|
+
require 'lita-github/filters'
|
24
|
+
|
25
|
+
module Lita
|
26
|
+
# Lita Handler
|
27
|
+
module Handlers
|
28
|
+
# GitHub Issues Lita Handler
|
29
|
+
class GithubIssues < Handler
|
30
|
+
include LitaGithub::General # Github handler common-use methods
|
31
|
+
include LitaGithub::Config # Github handler Lita configuration methods
|
32
|
+
include LitaGithub::Octo # Github handler common-use Octokit methods
|
33
|
+
include LitaGithub::Org # Github handler common-use Organization methods
|
34
|
+
include LitaGithub::Repo # Github handler common-use Repository methods
|
35
|
+
include LitaGithub::Filters # Github handler common-use method filters
|
36
|
+
|
37
|
+
on :loaded, :setup_octo # from LitaGithub::Octo
|
38
|
+
|
39
|
+
# issue states
|
40
|
+
LI_STATES = %w(open closed all)
|
41
|
+
# issue states
|
42
|
+
LI_SM = %w(created updated comments)
|
43
|
+
# sort direction
|
44
|
+
LI_DIR = %w(asc desc)
|
45
|
+
|
46
|
+
route(
|
47
|
+
/#{LitaGithub::R::A_REG}(?:issues|repo\s+?issues)\s+?#{LitaGithub::R::REPO_REGEX}/,
|
48
|
+
:issues_list,
|
49
|
+
command: true,
|
50
|
+
help: {
|
51
|
+
'gh issues PagerDuty/lita-github' => 'list the issues on a repo',
|
52
|
+
'gh issues PagerDuty/lita-github state:closed sort:updated sort:desc' => 'just showing some option usage'
|
53
|
+
}
|
54
|
+
)
|
55
|
+
|
56
|
+
# This is the handler for listing issues on a repository
|
57
|
+
# @author Tim Heckman <tim@pagerduty.com>
|
58
|
+
def issues_list(response)
|
59
|
+
full_name = rpo(*repo_match(response.match_data))
|
60
|
+
opts = opts_parse(response.message.body)
|
61
|
+
|
62
|
+
oops = validate_list_opts(opts)
|
63
|
+
|
64
|
+
return response.reply(oops) unless oops.empty?
|
65
|
+
|
66
|
+
return response.reply(t('repo_not_found', repo: full_name)) unless repo?(full_name)
|
67
|
+
|
68
|
+
# get the issues that are not pull requests
|
69
|
+
begin
|
70
|
+
issues = octo.list_issues(full_name, opts).reject { |i| i.key?(:pull_request) }
|
71
|
+
rescue Octokit::UnprocessableEntity => e
|
72
|
+
return response.reply(t('issues_list.invalid_opts', m: e.message))
|
73
|
+
rescue StandardError => e
|
74
|
+
return response.reply(t('boom', m: e.message))
|
75
|
+
end
|
76
|
+
|
77
|
+
if issues.empty?
|
78
|
+
reply = t('issues_list.none', r: full_name)
|
79
|
+
else
|
80
|
+
reply = t('issues_list.header', n: issues.length, r: full_name)
|
81
|
+
issues.each { |i| reply << t('issues_list.item', i.to_h.merge(u: i[:user][:login], r: full_name)) }
|
82
|
+
end
|
83
|
+
|
84
|
+
response.reply(reply)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
90
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
91
|
+
#
|
92
|
+
# Validate the command options
|
93
|
+
#
|
94
|
+
# @param opts [Hash] the key:value pairs from the command
|
95
|
+
# @return [String] the response to reply with if there was a validation failure
|
96
|
+
def validate_list_opts(opts)
|
97
|
+
resp = ''
|
98
|
+
resp << t('issues_list.val_states') if opts.key?(:state) && !LI_STATES.include?(opts[:state])
|
99
|
+
resp << t('issues_list.val_sort') if opts.key?(:sort) && !LI_SM.include?(opts[:sort])
|
100
|
+
resp << t('issues_list.val_direction') if opts.key?(:direction) && !LI_DIR.include?(opts[:direction])
|
101
|
+
resp = "Invalid option(s):\n#{resp}" unless resp.empty?
|
102
|
+
resp
|
103
|
+
end
|
104
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
105
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
106
|
+
end
|
107
|
+
|
108
|
+
Lita.register_handler(GithubIssues)
|
109
|
+
end
|
110
|
+
end
|
@@ -68,6 +68,42 @@ module Lita
|
|
68
68
|
}
|
69
69
|
)
|
70
70
|
|
71
|
+
# rubocop:disable Metrics/LineLength
|
72
|
+
route(
|
73
|
+
/#{LitaGithub::R::A_REG}org\s+?user\s+?add(?<org>\s+?[a-zA-Z0-9_\-]+)?(?<team>\s?[a-zA-Z0-9_\-]+)\s+?(?<username>[a-zA-Z0-9_\-]+)/,
|
74
|
+
:org_user_add,
|
75
|
+
command: true,
|
76
|
+
# confirmation: { allow_self: false },
|
77
|
+
help: {
|
78
|
+
'gh org user add PagerDuty everyone theckman' => 'add the user theckman to the PagerDuty/everyone team -- this requires confirmation from another user. NOTE: This will add the member to the organization if they are not already!!',
|
79
|
+
'gh org user add PagerDuty 42 theckman' => "same as above, except with the team's ID instead of the slug"
|
80
|
+
}
|
81
|
+
)
|
82
|
+
|
83
|
+
route(
|
84
|
+
/#{LitaGithub::R::A_REG}org\s+?user\s+?rm(?<org>\s+?[a-zA-Z0-9_\-]+)?(?<team>\s?[a-zA-Z0-9_\-]+)\s+?(?<username>[a-zA-Z0-9_\-]+)/,
|
85
|
+
:org_user_rm,
|
86
|
+
comamnd: true,
|
87
|
+
# confirmation: { allow_self: false },
|
88
|
+
help: {
|
89
|
+
'gh org team rm PagerDuty everyone theckman' => 'remove the user theckman from the PagerDuty/everyone team, if this is their last team will remove them from the org. Requires confirmation from another user.',
|
90
|
+
'gh org team rm PagerDuty 42 theckman' => "same as above, except with the team's ID instead of the slug"
|
91
|
+
}
|
92
|
+
)
|
93
|
+
# rubocop:enable Metrics/LineLength
|
94
|
+
|
95
|
+
route(
|
96
|
+
/#{LitaGithub::R::A_REG}org\s+?eject(?<org>\s+?[a-zA-Z0-9_\-]+)?(?<username>\s+?[a-zA-Z0-9_\-]+)/,
|
97
|
+
:org_eject_user,
|
98
|
+
command: true,
|
99
|
+
# confirmation: { allow_self: false },
|
100
|
+
help: {
|
101
|
+
'gh org eject PagerDuty theckman' => 'forcibly removes the user from all groups in the organization -- ' \
|
102
|
+
'this is meant for someone leaving the organization. Requires ' \
|
103
|
+
'confirmation from another user.'
|
104
|
+
}
|
105
|
+
)
|
106
|
+
|
71
107
|
def org_teams_list(response)
|
72
108
|
md = response.match_data
|
73
109
|
org = md[:org].nil? ? config.default_org : organization(md[:org].strip)
|
@@ -93,7 +129,7 @@ module Lita
|
|
93
129
|
def org_team_add(response)
|
94
130
|
return response.reply(t('method_disabled')) if func_disabled?(__method__)
|
95
131
|
|
96
|
-
opts =
|
132
|
+
opts = opts_parse(response.message.body)
|
97
133
|
vo = validate_team_add_opts(opts)
|
98
134
|
return response.reply(vo[:msg]) unless vo[:success]
|
99
135
|
|
@@ -131,6 +167,101 @@ module Lita
|
|
131
167
|
end
|
132
168
|
end
|
133
169
|
|
170
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
171
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
172
|
+
def org_user_add(response)
|
173
|
+
return response.reply(t('method_disabled')) if func_disabled?(__method__)
|
174
|
+
|
175
|
+
md = response.match_data
|
176
|
+
org, team, username = [organization(md['org'].strip), md['team'].strip, md['username']]
|
177
|
+
|
178
|
+
begin
|
179
|
+
user_id = octo.user(username)[:id]
|
180
|
+
rescue Octokit::NotFound
|
181
|
+
return response.reply(t('user_not_found', n: username))
|
182
|
+
end
|
183
|
+
|
184
|
+
return response.reply(t('nope')) if octo.user[:id] == user_id
|
185
|
+
|
186
|
+
begin
|
187
|
+
team_obj = octo.team(team_id(team, org))
|
188
|
+
rescue Octokit::NotFound
|
189
|
+
return response.reply(t('team_not_found', team: team))
|
190
|
+
end
|
191
|
+
|
192
|
+
begin
|
193
|
+
resp = octo.add_team_membership(team_obj[:id], username)
|
194
|
+
rescue StandardError => e
|
195
|
+
return response.reply(t('boom', m: e.message))
|
196
|
+
end
|
197
|
+
|
198
|
+
if resp
|
199
|
+
response.reply(t('org_user_add.added', u: username, o: org, t: team_obj[:name], s: team_obj[:slug]))
|
200
|
+
else
|
201
|
+
response.reply(t('org_user_add.failed', t: team_obj[:name]))
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def org_user_rm(response)
|
206
|
+
return response.reply(t('method_disabled')) if func_disabled?(__method__)
|
207
|
+
md = response.match_data
|
208
|
+
org, team, username = [organization(md['org'].strip), md['team'].strip, md['username']]
|
209
|
+
|
210
|
+
begin
|
211
|
+
user_id = octo.user(username)[:id]
|
212
|
+
rescue Octokit::NotFound
|
213
|
+
return response.reply(t('user_not_found', n: username))
|
214
|
+
end
|
215
|
+
|
216
|
+
return response.reply(t('nope')) if octo.user[:id] == user_id
|
217
|
+
|
218
|
+
begin
|
219
|
+
team = octo.team(team_id(team, org))
|
220
|
+
rescue Octokit::NotFound
|
221
|
+
return response.reply(t('team_not_found', team: team))
|
222
|
+
end
|
223
|
+
|
224
|
+
begin
|
225
|
+
resp = octo.remove_team_member(team[:id], username)
|
226
|
+
rescue StandardError => e
|
227
|
+
return response.reply(t('boom', m: e.message))
|
228
|
+
end
|
229
|
+
|
230
|
+
if resp
|
231
|
+
response.reply(t('org_user_rm.removed', u: username, o: org, t: team[:name], s: team[:slug]))
|
232
|
+
else
|
233
|
+
response.reply(t('org_user_rm.failed'), t: team[:name])
|
234
|
+
end
|
235
|
+
end
|
236
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
237
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
238
|
+
|
239
|
+
def org_eject_user(response)
|
240
|
+
return response.reply(t('method_disabled')) if func_disabled?(__method__)
|
241
|
+
md = response.match_data
|
242
|
+
org, username = [organization(md['org'].strip), md['username'].strip]
|
243
|
+
|
244
|
+
begin
|
245
|
+
user_id = octo.user(username)[:id]
|
246
|
+
rescue Octokit::NotFound
|
247
|
+
return response.reply(t('user_not_found', n: username))
|
248
|
+
end
|
249
|
+
|
250
|
+
return response.reply(t('nope')) if octo.user[:id] == user_id
|
251
|
+
|
252
|
+
begin
|
253
|
+
resp = octo.remove_organization_member(org, username)
|
254
|
+
rescue StandardError => e
|
255
|
+
return response.reply(t('boom', m: e.message))
|
256
|
+
end
|
257
|
+
|
258
|
+
if resp
|
259
|
+
response.reply(t('org_eject_user.ejected', user: username, org: org))
|
260
|
+
else
|
261
|
+
response.reply(t('org_eject_user.failed'))
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
134
265
|
private
|
135
266
|
|
136
267
|
def validate_team_add_opts(opts)
|
data/locales/en.yml
CHANGED
@@ -17,7 +17,7 @@ en:
|
|
17
17
|
company: "Company: %{c}\n"
|
18
18
|
location: "Located: %{l}\n"
|
19
19
|
account_info: "GitHub Admin: %{site_admin}, Repos: %{public_repos}, Gists: %{public_gists}\n"
|
20
|
-
user_info: "Following: %{following}, Followers: %{followers},
|
20
|
+
user_info: "Following: %{following}, Followers: %{followers}, Joined: %{created_at}"
|
21
21
|
github_repo:
|
22
22
|
method_disabled: "Sorry, this function has either been disabled or not enabled in the config"
|
23
23
|
not_found: "That repo (%{org}/%{repo}) was not found"
|
@@ -55,7 +55,8 @@ en:
|
|
55
55
|
exception: "An unexpected exception was hit during the GitHub API operation. Please make sure all arguments are proper and try again, or try checking the GitHub status (gh status)"
|
56
56
|
not_found: "Pull request #%{pr} on %{org}/%{repo} not found"
|
57
57
|
pr_info:
|
58
|
-
header: "%{repo} #%{number}: %{title} :: %{url}\n"
|
58
|
+
header: "%{repo} #%{number}: '%{title}' :: %{url}\n"
|
59
|
+
header_long: "%{repo} #%{number}: '%{title}' opened by %{u} :: %{url}\n"
|
59
60
|
status: "Opened By: %{user} | State: %{state_str} | Build: %{build_status}"
|
60
61
|
merged: " | Merged By: %{merged_by}\n"
|
61
62
|
mergeable: " | Mergeable: %{mergeable}\n"
|
@@ -71,6 +72,9 @@ en:
|
|
71
72
|
method_disabled: "Sorry, this function has either been disabled or not enabled in the config"
|
72
73
|
org_not_found: "The organization '%{org}' was not found. Does my user have ownership perms?"
|
73
74
|
team_not_found: "Unable to match any teams based on: %{team}"
|
75
|
+
user_not_found: "Unable to find the GitHub user %{n}"
|
76
|
+
nope: "No...\n\nಠ_ಠ"
|
77
|
+
boom: "I had a problem :( ... %{m}"
|
74
78
|
org_teams_list:
|
75
79
|
header: "Showing %{num_teams} team(s) for %{org}:\n"
|
76
80
|
team: "Name: %{name}, Slug: %{slug}, ID: %{id}, Perms: %{permission}\n"
|
@@ -82,3 +86,23 @@ en:
|
|
82
86
|
org_team_rm:
|
83
87
|
pass: "The '%{name}' team was deleted. Its ID was %{id}"
|
84
88
|
fail: "Something went wrong trying to delete the '%{name}' team. Is Github having issues?"
|
89
|
+
org_eject_user:
|
90
|
+
ejected: "Ejected %{user} out of %{org}"
|
91
|
+
failed: "Failed to eject the user from the organization for an unknown reason"
|
92
|
+
org_user_rm:
|
93
|
+
removed: "%{u} has been removed from the '%{o}/%{t}' (%{s}) team"
|
94
|
+
failed: "Failed to remove the user from the '%{t}' team for some unknown reason"
|
95
|
+
org_user_add:
|
96
|
+
added: "%{u} has been added to the '%{o}/%{t}' (%{s}) team"
|
97
|
+
failed: "Failed to add the user to the '%{t}' team for some unknown reason"
|
98
|
+
github_issues:
|
99
|
+
repo_not_found: "That repo (%{repo}) was not found"
|
100
|
+
boom: "I had a problem :( ... %{m}"
|
101
|
+
issues_list:
|
102
|
+
none: "There are no open issues for %{r}"
|
103
|
+
header: "Showing %{n} issue(s) for %{r}\n"
|
104
|
+
item: "%{r} #%{number}: '%{title}' opened by %{u} :: %{html_url}\n"
|
105
|
+
val_states: "Issues can be one of the following states: 'open', 'closed', or 'all'\n"
|
106
|
+
val_sort: "Issues can be sorted by one of the following: 'created', 'updated', 'comments'\n"
|
107
|
+
val_direction: "Issues can be ordered either 'asc' (ascending) or 'desc' (descending)\n"
|
108
|
+
invalid_opts: "An invalid option was provided, here's the error from Octokit:\n%{m}"
|