ytrello 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rubocop.yml +104 -0
- data/.travis.yml +9 -0
- data/Gemfile +2 -0
- data/README.md +117 -0
- data/VERSION +1 -0
- data/bin/ytrello +10 -0
- data/bin/ytrello-addurl +11 -0
- data/bin/ytrello-check +218 -0
- data/bin/ytrello-create +128 -0
- data/bin/ytrello-reassign +27 -0
- data/config/trello-creds.yml.template +3 -0
- data/devel/show_list_ids +8 -0
- data/lib/ytrello.rb +87 -0
- data/lib/ytrello/version.rb +4 -0
- data/ytrello.gemspec +49 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 65edc7ba791b0cd9457ea8335c2da006cb63dd3b
|
4
|
+
data.tar.gz: 280867290171ee8c6d67807578bfd8498810fa4c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f0655da07f7cda5f7132f465ec12551e37817dab3e63ea7912e5b1340c65440cb7f14f9a12e0c20c6c1c4f2cfa5cb86319f610e06d1614ed3201df4a7d50e486
|
7
|
+
data.tar.gz: eb3569f6e2cce36f0992070beb70dfa0a26c4f1f3336d22602670c00869cca702db4ca39062fca93eee2bb12082911bf8bb380ad63d73303bdc798dcf2db145b
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# NOTE: This is a copy from https://github.com/yast/yast-devtools/blob/master/ytools/y2tool/rubocop_yast_style.yml
|
2
|
+
|
3
|
+
################################################################################
|
4
|
+
#
|
5
|
+
# This part contains the shared Rubocop configuration for SUSE projects. It is
|
6
|
+
# maintained at https://github.com/SUSE/style-guides/blob/master/rubocop-suse.yml
|
7
|
+
#
|
8
|
+
# NOTE: some rules have been commented out, see the YaST specific changes
|
9
|
+
# at the end of the file!
|
10
|
+
#
|
11
|
+
################################################################################
|
12
|
+
|
13
|
+
# Disabled, would require too many changes in the current code
|
14
|
+
#Lint/EndAlignment:
|
15
|
+
# StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#lintendalignment
|
16
|
+
# AlignWith: variable
|
17
|
+
|
18
|
+
Metrics/AbcSize:
|
19
|
+
StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#metricsabcsize
|
20
|
+
Max: 30
|
21
|
+
|
22
|
+
Metrics/LineLength:
|
23
|
+
StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#metricslinelength
|
24
|
+
Max: 100
|
25
|
+
# To make it possible to copy or click on URIs in the code, we allow lines
|
26
|
+
# contaning a URI to be longer than Max.
|
27
|
+
AllowURI: true
|
28
|
+
URISchemes:
|
29
|
+
- http
|
30
|
+
- https
|
31
|
+
|
32
|
+
Style/AlignHash:
|
33
|
+
StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#stylealignhash
|
34
|
+
EnforcedHashRocketStyle: table
|
35
|
+
EnforcedColonStyle: table
|
36
|
+
|
37
|
+
# Disabled, see the YaST default at the end of the file
|
38
|
+
#Style/AlignParameters:
|
39
|
+
# StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#stylealignparameters
|
40
|
+
# Enabled: false
|
41
|
+
|
42
|
+
Style/CollectionMethods:
|
43
|
+
StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#stylecollectionmethods
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Style/EmptyLinesAroundBlockBody:
|
47
|
+
StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#styleemptylinesaroundblockbody
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
Style/MultilineOperationIndentation:
|
51
|
+
StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#stylemultilineoperationindentation
|
52
|
+
EnforcedStyle: indented
|
53
|
+
|
54
|
+
Style/StringLiterals:
|
55
|
+
StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#stylestringliterals
|
56
|
+
EnforcedStyle: double_quotes
|
57
|
+
|
58
|
+
Style/StringLiteralsInInterpolation:
|
59
|
+
StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#stylestringliteralsininterpolation
|
60
|
+
EnforcedStyle: double_quotes
|
61
|
+
|
62
|
+
Style/WordArray:
|
63
|
+
StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#deviations-from-the-upstream-style-guide
|
64
|
+
Enabled: false
|
65
|
+
|
66
|
+
Style/RegexpLiteral:
|
67
|
+
StyleGuide: https://github.com/SUSE/style-guides/blob/master/Ruby.md#deviations-from-the-upstream-style-guide
|
68
|
+
Enabled: false
|
69
|
+
|
70
|
+
Style/SignalException:
|
71
|
+
StyleGuide: https://github.com/bbatsov/ruby-style-guide#fail-method
|
72
|
+
EnforcedStyle: only_raise
|
73
|
+
|
74
|
+
|
75
|
+
################################################################################
|
76
|
+
#
|
77
|
+
# This part contains the YaST specific changes to the shared SUSE configuration
|
78
|
+
#
|
79
|
+
################################################################################
|
80
|
+
|
81
|
+
# no extra indentation for multiline function calls
|
82
|
+
Style/AlignParameters:
|
83
|
+
EnforcedStyle: with_fixed_indentation
|
84
|
+
|
85
|
+
# no extra indentation for case
|
86
|
+
Style/CaseIndentation:
|
87
|
+
IndentWhenRelativeTo: end
|
88
|
+
|
89
|
+
# "unless" has a different connotation than "if not"
|
90
|
+
Style/NegatedIf:
|
91
|
+
Enabled: false
|
92
|
+
|
93
|
+
# allow more than 10 lines for methods
|
94
|
+
Metrics/MethodLength:
|
95
|
+
Max: 30
|
96
|
+
|
97
|
+
Metrics/ClassLength:
|
98
|
+
Max: 250
|
99
|
+
|
100
|
+
AllCops:
|
101
|
+
# do not check the locally installed gems
|
102
|
+
Exclude:
|
103
|
+
- '.vendor/**/*'
|
104
|
+
- 'vendor/**/*'
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# YTrello
|
2
|
+
|
3
|
+
Tools to help with the YaST Trello boards.
|
4
|
+
|
5
|
+
- Make cards for bugs in Bugzilla.suse.com
|
6
|
+
|
7
|
+
### Requirements
|
8
|
+
|
9
|
+
- [bicho.gem][b] >= 0.0.10
|
10
|
+
- [ruby-trello.gem][t]
|
11
|
+
- [python-bugzilla][p], with the SUSE flavor
|
12
|
+
|
13
|
+
[b]: https://github.com/dmacvicar/bicho
|
14
|
+
[t]: https://github.com/jeremytregunna/ruby-trello
|
15
|
+
[p]: https://build.opensuse.org/package/show/openSUSE:Factory/python-bugzilla
|
16
|
+
|
17
|
+
### Installation
|
18
|
+
|
19
|
+
From a Git checkout:
|
20
|
+
|
21
|
+
```sh
|
22
|
+
sudo zypper install python-bugzilla
|
23
|
+
bundle install
|
24
|
+
alias ytrello="bundle exec ytrello"
|
25
|
+
```
|
26
|
+
|
27
|
+
From Rubygems:
|
28
|
+
|
29
|
+
```sh
|
30
|
+
sudo zypper install python-bugzilla
|
31
|
+
gem install ytrello
|
32
|
+
```
|
33
|
+
|
34
|
+
From OBS (TODO, does not exist yet!), this selects the appropriate Ruby version:
|
35
|
+
|
36
|
+
```sh
|
37
|
+
zypper install -C 'rubygem(ytrello)'
|
38
|
+
```
|
39
|
+
|
40
|
+
### Setup
|
41
|
+
|
42
|
+
**ruby-trello** reads the configuration file `~/.config/trello-creds.yml`:
|
43
|
+
|
44
|
+
- The Developer Public Key is the first hex string on
|
45
|
+
<https://trello.com/app-key>
|
46
|
+
|
47
|
+
```sh
|
48
|
+
F=~/.config/trello-creds.yml`
|
49
|
+
touch $F
|
50
|
+
chmod 700 $F
|
51
|
+
echo "# https://github.com/mvidner/ytrello" >> $F
|
52
|
+
echo "TRELLO_DEVELOPER_PUBLIC_KEY: replaceme" >> $F
|
53
|
+
vi $F
|
54
|
+
```
|
55
|
+
|
56
|
+
After you fill in the developer key, use this to request an app token, then
|
57
|
+
copy the generated token to the config file.
|
58
|
+
|
59
|
+
```sh
|
60
|
+
xdg-open "https://trello.com/1/authorize?key=REPLACEME&name=ytrello&expiration=never&response_type=token&scope=read,write"
|
61
|
+
echo "TRELLO_MEMBER_TOKEN: replaceme" >>$F
|
62
|
+
vi $F
|
63
|
+
```
|
64
|
+
|
65
|
+
For backward compatibility, the environment variables
|
66
|
+
`TRELLO_DEVELOPER_PUBLIC_KEY` and `TRELLO_MEMBER_TOKEN` are respected, and
|
67
|
+
even preferred over the configuration file.
|
68
|
+
|
69
|
+
**bicho** and **python-bugzilla** read ~/.oscrc so if you have used `osc` it
|
70
|
+
should work already.
|
71
|
+
|
72
|
+
## Usage
|
73
|
+
|
74
|
+
- **create**
|
75
|
+
|
76
|
+
```sh
|
77
|
+
ytrello create $BUG_NUMBER
|
78
|
+
```
|
79
|
+
|
80
|
+
|
81
|
+
- **addurl** is normally called by **create**,
|
82
|
+
but in case you want to use it directly, it is straightforward. It will
|
83
|
+
assign the **URL** field of a bug unless the field is already present.
|
84
|
+
|
85
|
+
```sh
|
86
|
+
ytrello addurl 999999 https://trello.example.com/cards/my-first-card
|
87
|
+
```
|
88
|
+
|
89
|
+
- **check** runs some validation checks and reports the found issues:
|
90
|
+
|
91
|
+
- It checks whether the YaST team bugs have a link to a Trello card (in the
|
92
|
+
**URL** field).
|
93
|
+
- It checks that all open YaST team bugs are tracked in Trello (i.e. a card
|
94
|
+
exists).
|
95
|
+
- Reports the Trello cards which refer to a closed bug in bugzilla. These
|
96
|
+
cards should be moved to *Done* or archived if the bug is not valid anymore.
|
97
|
+
|
98
|
+
The command supports a simple auto correction mode. In this mode it tries
|
99
|
+
to fix the found issues. Use `-a` or `--auto-correct` option to turn it on
|
100
|
+
(by default it is disabled).
|
101
|
+
|
102
|
+
Currently these auto correct actions are performed:
|
103
|
+
|
104
|
+
- The missing links from Bugzilla bugs to the Trello cards are added.
|
105
|
+
- Missing Trello cards are created
|
106
|
+
|
107
|
+
It is recommended to run it in read-only mode first to see the found issues:
|
108
|
+
|
109
|
+
```sh
|
110
|
+
ytrello check
|
111
|
+
```
|
112
|
+
|
113
|
+
If the reported changes are valid you can fix them by running:
|
114
|
+
|
115
|
+
```sh
|
116
|
+
ytrello check -a
|
117
|
+
```
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.18.0
|
data/bin/ytrello
ADDED
data/bin/ytrello-addurl
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Add a [related] URL field to a bug
|
3
|
+
bug_num = ARGV[0]
|
4
|
+
card_url = ARGV[1]
|
5
|
+
|
6
|
+
has_url = `bugzilla query --outputformat='%{bug_file_loc}' -b #{bug_num}`.chomp
|
7
|
+
|
8
|
+
if has_url.empty?
|
9
|
+
system "bugzilla", "modify", "--url", card_url, bug_num
|
10
|
+
puts "Card URL added to Bugzilla"
|
11
|
+
end
|
data/bin/ytrello-check
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "ytrello"
|
3
|
+
|
4
|
+
require "rainbow"
|
5
|
+
require "optparse"
|
6
|
+
|
7
|
+
# If a Trello card contains just a number without "FATE" (case insensitive)
|
8
|
+
# it might be a FATE number instead of a bug number. Consider it as a possible
|
9
|
+
# FATE number if it is below this limit.
|
10
|
+
FATE_NUMBER_MAX = 400_000
|
11
|
+
|
12
|
+
def parse_options
|
13
|
+
options = {}
|
14
|
+
|
15
|
+
op = OptionParser.new do |opts|
|
16
|
+
opts.banner =
|
17
|
+
"\nThis script checks whether the YaST Trello boards are in sync with the Bugzilla.\n\n" \
|
18
|
+
"The Trello credentials are read from the #{ENV_TRELLO_KEY} and #{ENV_TRELLO_TOKEN}\n" \
|
19
|
+
"environment variables. The Bugzilla credentials are read from the '~/.oscrc' file.\n\n" \
|
20
|
+
"Usage: #{$PROGRAM_NAME} [options]\n\n"
|
21
|
+
|
22
|
+
opts.on("-a", "--auto-correct", "Try to fix the detected problems, " \
|
23
|
+
"currently only the missing Trello cards are created") do
|
24
|
+
|
25
|
+
options[:auto_correct] = true
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-h", "--help", "Print this help") do
|
29
|
+
puts opts
|
30
|
+
exit 0
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
op.parse!
|
35
|
+
|
36
|
+
options
|
37
|
+
end
|
38
|
+
|
39
|
+
# query the bugs assigned to the YaST Team
|
40
|
+
# @return [Hash<Symbol,Array<BichoBug>>] :all => all team bugs, :missing =>
|
41
|
+
# bugs with empty URL attribute, :unknown => the URL is set, but does not
|
42
|
+
# point to a Trello URL
|
43
|
+
def check_team_bugs
|
44
|
+
missing_bugs = []
|
45
|
+
unknown_links = []
|
46
|
+
|
47
|
+
# ignore closed bugs
|
48
|
+
all_bugs = Bicho::Bug.where(assigned_to: BUGZILLA_ACCOUNT).select do |bug|
|
49
|
+
bug.resolution.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
all_bugs.each do |bug|
|
53
|
+
if bug["url"].nil? || bug["url"].empty?
|
54
|
+
missing_bugs << bug
|
55
|
+
elsif !bug["url"].include?("https://trello.com/")
|
56
|
+
unknown_links << bug
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
{ all: all_bugs, missing: missing_bugs, unknown: unknown_links }
|
61
|
+
end
|
62
|
+
|
63
|
+
# read all Trello cards from the Incoming and the Team boards
|
64
|
+
# @return [Array<Trello::Card>] found cards
|
65
|
+
def trello_cards
|
66
|
+
inc_board = Trello::Board.find(INC_BOARD_ID) || raise
|
67
|
+
t1_board = Trello::Board.find(TEAM_1_BOARD_ID) || raise
|
68
|
+
ta_board = Trello::Board.find(TEAM_A_BOARD_ID) || raise
|
69
|
+
|
70
|
+
to_array(inc_board.cards) + to_array(t1_board.cards) + to_array(ta_board.cards)
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [Hash<Fixnum,Trello::Card>] map bug ID => Trello card
|
74
|
+
def find_trello_bugs
|
75
|
+
trello_bugs = {}
|
76
|
+
|
77
|
+
trello_cards.each do |card|
|
78
|
+
# skip cards not in the specified list
|
79
|
+
next unless CHECKED_LISTS.include?(card.list_id)
|
80
|
+
|
81
|
+
# TODO: skip FATE cards for now, maybe add a FATE check as well?
|
82
|
+
next if card.name.match(/\bfate\b/i) || card.name.match(/\bfeature\b/i)
|
83
|
+
|
84
|
+
# skip cards not containing any bug number
|
85
|
+
# expect bug numbers have 6 or 7 digits
|
86
|
+
next unless card.name =~ /\b#?(\d{6,7})\b/
|
87
|
+
|
88
|
+
bug_id = Regexp.last_match[1].to_i
|
89
|
+
trello_bugs[bug_id] = card
|
90
|
+
end
|
91
|
+
|
92
|
+
trello_bugs
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_closed_trello_bugs(found_trello_bugs)
|
96
|
+
closed_bugs = {}
|
97
|
+
# ignore "small" numbers, they most probably refer to a feature
|
98
|
+
bugnumbers = found_trello_bugs.keys.select { |n| n > FATE_NUMBER_MAX }
|
99
|
+
|
100
|
+
Bicho.client.get_bugs(*bugnumbers).each do |bug|
|
101
|
+
next if bug.resolution.empty?
|
102
|
+
closed_bugs[bug] = found_trello_bugs[bug.id]
|
103
|
+
end
|
104
|
+
|
105
|
+
closed_bugs
|
106
|
+
end
|
107
|
+
|
108
|
+
def print_bugzilla_summary(team_bugs)
|
109
|
+
team_bugs[:missing].each do |bug|
|
110
|
+
puts "Bug #{Rainbow(bug.url).yellow}: URL attribute not set"
|
111
|
+
end
|
112
|
+
|
113
|
+
team_bugs[:unknown].each do |bug|
|
114
|
+
puts "Bug #{Rainbow(bug.url).yellow}: URL \"#{bug["url"]}\"does not link to Trello "
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# check if a number in Trello card label possibly refers to FATE instead of bugzilla
|
119
|
+
def print_trello_fate_warnings(open_trello_bugs)
|
120
|
+
open_trello_bugs.each do |bug_id, card|
|
121
|
+
# enough high number or "bug" in the name
|
122
|
+
next if bug_id > FATE_NUMBER_MAX || card.name.match(/\bbug\b/i)
|
123
|
+
|
124
|
+
puts "#{Rainbow(card.short_url).yellow} - " \
|
125
|
+
"#{card.name.sub(bug_id.to_s, Rainbow(bug_id.to_s).magenta)}"
|
126
|
+
puts "Number #{Rainbow(bug_id).magenta} possibly refers to " \
|
127
|
+
"FATE, add \"FATE\" string if it is a feature number"
|
128
|
+
puts
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def print_trello_closed_bugs(trello_closed_bugs)
|
133
|
+
trello_closed_bugs.each do |bug, card|
|
134
|
+
puts Rainbow(card.name).cyan
|
135
|
+
puts "#{Rainbow(card.short_url).yellow} refers to #{Rainbow(bug.url).yellow}" \
|
136
|
+
" resolved as #{Rainbow(bug.resolution).red}"
|
137
|
+
puts
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def print_summary_line(line, ok)
|
142
|
+
puts Rainbow(line).color(ok ? :green : :red)
|
143
|
+
end
|
144
|
+
|
145
|
+
def print_summary(team_bugs, open_trello_bugs, closed_trello_bugs)
|
146
|
+
not_in_trello = bugs_not_in_trello(team_bugs, open_trello_bugs)
|
147
|
+
|
148
|
+
puts
|
149
|
+
print_summary_line("Found #{not_in_trello.size} bugs not in Trello", not_in_trello.empty?)
|
150
|
+
print_summary_line("Found #{team_bugs[:all].size} YaST Team bugs, " \
|
151
|
+
"#{team_bugs[:missing].size} do not link to Trello.", team_bugs[:missing].empty?)
|
152
|
+
print_summary_line("Found #{open_trello_bugs.size} bug cards in Trello, " \
|
153
|
+
"#{closed_trello_bugs.size} possibly refer to a closed bug.", closed_trello_bugs.empty?)
|
154
|
+
puts
|
155
|
+
end
|
156
|
+
|
157
|
+
def bugs_not_in_trello(team_bugs, open_trello_bugs)
|
158
|
+
team_bugs[:all].map(&:id) - open_trello_bugs.keys.map(&:to_i)
|
159
|
+
end
|
160
|
+
|
161
|
+
def print_not_in_trello(not_in_trello)
|
162
|
+
not_in_trello.each do |bug|
|
163
|
+
bug_url = "https://bugzilla.suse.com/#{bug}"
|
164
|
+
puts "Bug #{Rainbow(bug_url).yellow} is not tracked in Trello"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def print_result(team_bugs, open_trello_bugs, closed_trello_bugs)
|
169
|
+
not_in_trello = bugs_not_in_trello(team_bugs, open_trello_bugs)
|
170
|
+
print_not_in_trello(not_in_trello)
|
171
|
+
print_bugzilla_summary(team_bugs)
|
172
|
+
print_trello_fate_warnings(open_trello_bugs)
|
173
|
+
print_trello_closed_bugs(closed_trello_bugs)
|
174
|
+
print_summary(team_bugs, open_trello_bugs, closed_trello_bugs)
|
175
|
+
end
|
176
|
+
|
177
|
+
######################################################
|
178
|
+
|
179
|
+
options = parse_options
|
180
|
+
|
181
|
+
setup_bicho
|
182
|
+
setup_trello
|
183
|
+
|
184
|
+
puts "Reading the bugs assigned to #{BUGZILLA_ACCOUNT}..."
|
185
|
+
team_bugs = check_team_bugs
|
186
|
+
|
187
|
+
puts "Reading the YaST Trello cards..."
|
188
|
+
trello_bugs = find_trello_bugs
|
189
|
+
|
190
|
+
puts "Reading bugs referred in the YaST Trello cards..."
|
191
|
+
closed_trello_bugs = find_closed_trello_bugs(trello_bugs)
|
192
|
+
not_in_trello = bugs_not_in_trello(team_bugs, trello_bugs)
|
193
|
+
|
194
|
+
print_result(team_bugs, trello_bugs, closed_trello_bugs)
|
195
|
+
|
196
|
+
# no issue found
|
197
|
+
exit 0 if team_bugs[:missing].empty? && closed_trello_bugs.empty? && not_in_trello.empty?
|
198
|
+
|
199
|
+
# try fixing some problems
|
200
|
+
if options[:auto_correct]
|
201
|
+
puts "Running autocorrection..."
|
202
|
+
create_script = File.expand_path("../create", __FILE__)
|
203
|
+
|
204
|
+
team_bugs[:missing].each do |bug|
|
205
|
+
puts "Creating a Trello card for bug ##{bug.id}..."
|
206
|
+
`#{create_script} #{bug.id}`
|
207
|
+
end
|
208
|
+
|
209
|
+
not_in_trello.each do |bug|
|
210
|
+
puts "Creating a Trello card for bug ##{bug}..."
|
211
|
+
`#{create_script} #{bug}`
|
212
|
+
end
|
213
|
+
|
214
|
+
# potentially fixed everything?
|
215
|
+
exit 0 if closed_trello_bugs.empty?
|
216
|
+
end
|
217
|
+
|
218
|
+
exit 1
|
data/bin/ytrello-create
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "ytrello"
|
3
|
+
require "English"
|
4
|
+
|
5
|
+
if ARGV.empty? || ARGV[0].include?("-h")
|
6
|
+
puts "Usage: #{$PROGRAM_NAME} bug_number"
|
7
|
+
puts " Creates a trello card for a bug, links the bug to the card"
|
8
|
+
exit 0
|
9
|
+
end
|
10
|
+
|
11
|
+
setup_trello
|
12
|
+
setup_bicho
|
13
|
+
|
14
|
+
# abbreviate a SUSE product name
|
15
|
+
def abbrev(s)
|
16
|
+
s
|
17
|
+
.sub("openSUSE", "oS")
|
18
|
+
.sub("SUSE Linux Enterprise Desktop", "SLED")
|
19
|
+
.sub("SUSE Linux Enterprise Server", "SLES")
|
20
|
+
.sub("SUSE Container as a Service Platform 1.0", "CaaSP1")
|
21
|
+
.sub(/\(.*\)/, "") # remove superfluous abbreviation
|
22
|
+
.sub(" SP", "-SP")
|
23
|
+
.sub(" Factory", "-TW") # Tumbleweed
|
24
|
+
.tr(" ", "")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Trello list IDs, see also show_list_ids
|
28
|
+
PRODUCT_LISTS = {
|
29
|
+
/SLE[SD]15/ => "5952060e0e9190605c75863e", # SLE 15
|
30
|
+
# "59a3db0f0fac7c99d1808ae9" is "SLE 15 Storage" but we can't autodetect that
|
31
|
+
/SLE[SD]12-SP3/ => "57cfdbcc9ae10f3d1fb996d3", # SLE-12-SP3 Maintenance
|
32
|
+
/SLE[SD]12-SP2/ => "5538994821027776154180eb", # SLE12-SP2 Maintenance
|
33
|
+
/SLE[SD]12-SP1/ => "5502d6719b0d5db70bcf6655", # SLE12-SP1 maintenance
|
34
|
+
/SLE[SD]12/ => "5507f04f2c885ffbdd53208a", # SLE12-maintenance
|
35
|
+
/SLE[SD]11/ => "5507f140ab44b6bcfcc6c561", # SLE11-maintenance
|
36
|
+
/^oS/ => "550800984de3079fa9ded12a", # openSUSE
|
37
|
+
/CaaSP1/ => "5877cf5650f2787cf6eb25a1", # CaaSP 1.0
|
38
|
+
# fallback
|
39
|
+
/./ => "5507f28d31c1cfac7a83eb72" # Generic Ideas
|
40
|
+
}.freeze
|
41
|
+
|
42
|
+
def product_to_list(product)
|
43
|
+
PRODUCT_LISTS.to_a.each do |pattern, list_id|
|
44
|
+
return list_id if product =~ pattern
|
45
|
+
end
|
46
|
+
raise "Internal error, PRODUCT_LISTS did not match"
|
47
|
+
end
|
48
|
+
|
49
|
+
def markdown_link(text, url)
|
50
|
+
"[#{text}](#{url})"
|
51
|
+
end
|
52
|
+
|
53
|
+
def bz_markdown_link(id)
|
54
|
+
markdown_link("bsc##{id}", "#{BUGZILLA_URL}/show_bug.cgi?id=#{id}")
|
55
|
+
end
|
56
|
+
|
57
|
+
def bicho_details(bug_id)
|
58
|
+
bug = Bicho.client.get_bugs(bug_id).first
|
59
|
+
raise "Bug ##{bug_id} not found" unless bug
|
60
|
+
bug.priority =~ /^(\S+)\s/
|
61
|
+
{ summary: bug.summary, product: bug.product, priority: Regexp.last_match(1) }
|
62
|
+
end
|
63
|
+
|
64
|
+
# create a card description text
|
65
|
+
# @param bug_id bug number
|
66
|
+
# @return [String] description text in the Markdown format
|
67
|
+
def card_description(bug_id)
|
68
|
+
descr = <<EOT
|
69
|
+
Bugzilla: #{bz_markdown_link(bug_id)}
|
70
|
+
|
71
|
+
---
|
72
|
+
## Review
|
73
|
+
- Pull Request: *URL here*
|
74
|
+
EOT
|
75
|
+
|
76
|
+
# just to avoid the trailing blank errors in the heredoc
|
77
|
+
descr + "- "
|
78
|
+
end
|
79
|
+
|
80
|
+
bug_id = ARGV[0]
|
81
|
+
details = bicho_details(bug_id)
|
82
|
+
product = abbrev(details[:product])
|
83
|
+
card_name = "#{product} (#{details[:priority]}) ##{bug_id} #{details[:summary]}"
|
84
|
+
description = card_description(bug_id)
|
85
|
+
list_id = product_to_list(product)
|
86
|
+
# list_id = "546336636415e12617f88e47" # My work / Done
|
87
|
+
|
88
|
+
debug "Trello query"
|
89
|
+
inc_board = Trello::Board.find(INC_BOARD_ID) || raise
|
90
|
+
t1_board = Trello::Board.find(TEAM_1_BOARD_ID) || raise
|
91
|
+
ta_board = Trello::Board.find(TEAM_A_BOARD_ID) || raise
|
92
|
+
|
93
|
+
labels = inc_board.labels(false) # false: objects; true: names
|
94
|
+
new_item_label = labels.find { |i| i.name =~ /new.item/i } || raise
|
95
|
+
|
96
|
+
list = Trello::List.find(list_id) ||
|
97
|
+
raise("Cannot find list #{list_id} to represent #{details[:product]}")
|
98
|
+
|
99
|
+
inc_cards = to_array inc_board.cards
|
100
|
+
t1_cards = to_array t1_board.cards
|
101
|
+
ta_cards = to_array ta_board.cards
|
102
|
+
|
103
|
+
existing = (inc_cards + t1_cards + ta_cards).find_all do |c|
|
104
|
+
c.name =~ /\D#{bug_id}\D/
|
105
|
+
end
|
106
|
+
|
107
|
+
card = nil
|
108
|
+
if existing.empty?
|
109
|
+
debug "Creating"
|
110
|
+
|
111
|
+
raise "Trying to add to already archived list '#{list.name}'" if list.closed
|
112
|
+
|
113
|
+
card = Trello::Card.create(list_id: list_id,
|
114
|
+
name: card_name,
|
115
|
+
desc: description,
|
116
|
+
pos: "top")
|
117
|
+
card.add_label(new_item_label)
|
118
|
+
puts "Created #{bug_id} => #{card.short_url}"
|
119
|
+
else
|
120
|
+
puts "Card for bug already exists:"
|
121
|
+
existing.each { |c| puts c.url }
|
122
|
+
card = existing.first if existing.size == 1
|
123
|
+
end
|
124
|
+
|
125
|
+
if card
|
126
|
+
debug "Assigning card URL in Bugzilla"
|
127
|
+
system "ytrello", "addurl", bug_id.to_s, card.short_url.to_s
|
128
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
bug_num = ARGV[0]
|
4
|
+
FROM = "yast2-maintainers@suse.de".freeze
|
5
|
+
TO = "yast-internal@suse.de".freeze
|
6
|
+
|
7
|
+
if bug_num.nil? || bug_num == "-h"
|
8
|
+
puts <<TXT
|
9
|
+
Usage: ytrello-reassign BUGNUM
|
10
|
+
|
11
|
+
Reassign a bug
|
12
|
+
from the incoming account #{FROM}
|
13
|
+
to the scrum account #{TO}
|
14
|
+
Does nothing if the original assignee is different.
|
15
|
+
TXT
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
|
19
|
+
assignee = `bugzilla query --outputformat='%{assigned_to}' -b #{bug_num}`
|
20
|
+
if assignee == FROM
|
21
|
+
puts "Changing assignee from #{assignee} to #{TO}"
|
22
|
+
system "bugzilla", "modify", "--assignee", TO, bug_num
|
23
|
+
elsif assignee == TO
|
24
|
+
puts "Assignee #{assignee} is already correct; unchanged"
|
25
|
+
else
|
26
|
+
puts "Assignee #{assignee} unchanged"
|
27
|
+
end
|
data/devel/show_list_ids
ADDED
data/lib/ytrello.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require "trello"
|
2
|
+
require "bicho"
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
# Trello board IDs
|
6
|
+
INC_BOARD_ID = "5507f013b863aa041618871d".freeze # Agile YaST Incoming Board
|
7
|
+
TEAM_1_BOARD_ID = "5502d5dd8eb45fb4581c1a0f".freeze # Agile YaST: Team 1
|
8
|
+
TEAM_A_BOARD_ID = "557833ad6be7b9634f089201".freeze # Agile YaST: Team A
|
9
|
+
|
10
|
+
# Trello list IDs, see also show_list_ids
|
11
|
+
CHECKED_LISTS = [
|
12
|
+
# Incoming board, keep sorted same as incomming board
|
13
|
+
# Backlog Team A
|
14
|
+
"5502d691d05c3b3817317566",
|
15
|
+
# Backlog Team 1
|
16
|
+
"557835b5cb9c13dcd032ecbb",
|
17
|
+
# SLE15 development
|
18
|
+
"5952060e0e9190605c75863e",
|
19
|
+
# SLE15 Storage development
|
20
|
+
"59a3db0f0fac7c99d1808ae9",
|
21
|
+
# SLE12-SP3 maintenance"
|
22
|
+
"57cfdbcc9ae10f3d1fb996d3",
|
23
|
+
# SLE12-SP2 development
|
24
|
+
"5538994821027776154180eb",
|
25
|
+
# Generic Ideas
|
26
|
+
"5507f28d31c1cfac7a83eb72",
|
27
|
+
# SLE12-maintenance
|
28
|
+
"5507f04f2c885ffbdd53208a",
|
29
|
+
# SLE12-SP1 maintenance
|
30
|
+
"5502d6719b0d5db70bcf6655",
|
31
|
+
# SLE11-maintenance
|
32
|
+
"5507f140ab44b6bcfcc6c561",
|
33
|
+
# openSUSE
|
34
|
+
"550800984de3079fa9ded12a",
|
35
|
+
|
36
|
+
# Team 1 boards
|
37
|
+
# Sprint Backlog
|
38
|
+
"5577ed07930f16fb224ca248",
|
39
|
+
# Doing
|
40
|
+
"5502d6b29a7a2ab8025a4c56",
|
41
|
+
|
42
|
+
# Team A boards
|
43
|
+
# Sprint Backlog
|
44
|
+
"5502d69d3e68ab3d1729337e",
|
45
|
+
# Doing
|
46
|
+
"557833dde4f1218b7d1cf831"
|
47
|
+
].freeze
|
48
|
+
|
49
|
+
BUGZILLA_URL = "https://bugzilla.suse.com".freeze
|
50
|
+
BUGZILLA_ACCOUNT = "yast-internal@suse.de".freeze
|
51
|
+
|
52
|
+
ENV_TRELLO_KEY = "TRELLO_DEVELOPER_PUBLIC_KEY".freeze
|
53
|
+
ENV_TRELLO_TOKEN = "TRELLO_MEMBER_TOKEN".freeze
|
54
|
+
|
55
|
+
# set the SUSE Bugzilla connection
|
56
|
+
def setup_bicho
|
57
|
+
Bicho.client = Bicho::Client.new(BUGZILLA_URL)
|
58
|
+
end
|
59
|
+
|
60
|
+
# set the Trello credentials
|
61
|
+
def setup_trello
|
62
|
+
fn = "#{ENV["HOME"]}/.config/trello-creds.yml"
|
63
|
+
text = File.exist?(fn) ? File.read(fn) : ""
|
64
|
+
creds = Hash(YAML.safe_load(text))
|
65
|
+
key = ENV[ENV_TRELLO_KEY] || creds[ENV_TRELLO_KEY]
|
66
|
+
token = ENV[ENV_TRELLO_TOKEN] || creds[ENV_TRELLO_TOKEN]
|
67
|
+
unless key && token
|
68
|
+
msg = "Error: Pass the Trello credentials via #{ENV_TRELLO_KEY} and\n" \
|
69
|
+
" #{ENV_TRELLO_TOKEN}, either in #{fn} or in environment variables."
|
70
|
+
$stderr.puts msg
|
71
|
+
exit 1
|
72
|
+
end
|
73
|
+
|
74
|
+
Trello.configure do |config|
|
75
|
+
config.developer_public_key = key
|
76
|
+
config.member_token = token
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Array#to_a -> Trello::MultiAssociation, WTF?!
|
81
|
+
def to_array(a)
|
82
|
+
a.map { |i| i }
|
83
|
+
end
|
84
|
+
|
85
|
+
def debug(s)
|
86
|
+
$stderr.puts s if $VERBOSE
|
87
|
+
end
|
data/ytrello.gemspec
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + "/lib/ytrello/version")
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ytrello"
|
7
|
+
s.version = Ytrello::VERSION
|
8
|
+
s.summary = "Tools to help with the YaST Trello boards and Bugzilla"
|
9
|
+
s.description = <<TXT
|
10
|
+
Ytrello are tools to help my Scrum team manage Trello cards and Bugzilla bugs.
|
11
|
+
Much of the configuration is harcoded now, yuck!
|
12
|
+
TXT
|
13
|
+
|
14
|
+
s.author = "Martin Vidner"
|
15
|
+
s.email = "martin@vidner.net"
|
16
|
+
s.homepage = "https://github.com/mvidner/ytrello"
|
17
|
+
s.license = "MIT"
|
18
|
+
|
19
|
+
# ruby -e 'puts `git ls-files`.lines.map { |f| " %s" % f.strip.inspect }.join ",\n"'
|
20
|
+
s.files = [
|
21
|
+
".gitignore",
|
22
|
+
".rubocop.yml",
|
23
|
+
".travis.yml",
|
24
|
+
"Gemfile",
|
25
|
+
"Gemfile.lock",
|
26
|
+
"README.md",
|
27
|
+
"VERSION",
|
28
|
+
"bin/ytrello",
|
29
|
+
"bin/ytrello-addurl",
|
30
|
+
"bin/ytrello-check",
|
31
|
+
"bin/ytrello-create",
|
32
|
+
"bin/ytrello-reassign",
|
33
|
+
"config/trello-creds.yml.template",
|
34
|
+
"devel/show_list_ids",
|
35
|
+
"lib/ytrello.rb",
|
36
|
+
"lib/ytrello/version.rb",
|
37
|
+
"ytrello.gemspec"
|
38
|
+
]
|
39
|
+
|
40
|
+
s.executables = s.files.grep(/^bin\//) { |f| File.basename(f) }
|
41
|
+
|
42
|
+
s.requirements << "python-bugzilla, with the SUSE flavor"
|
43
|
+
|
44
|
+
s.add_dependency "bicho", ">= 0.0.10"
|
45
|
+
s.add_dependency "ruby-trello", "~> 1.3.0"
|
46
|
+
s.add_dependency "rainbow", ">= 2.0.0"
|
47
|
+
|
48
|
+
s.add_development_dependency "rubocop", "0.41.2"
|
49
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ytrello
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.18.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Vidner
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-02-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bicho
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.10
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.10
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-trello
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.3.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.3.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rainbow
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.0.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.0.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.41.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.41.2
|
69
|
+
description: |
|
70
|
+
Ytrello are tools to help my Scrum team manage Trello cards and Bugzilla bugs.
|
71
|
+
Much of the configuration is harcoded now, yuck!
|
72
|
+
email: martin@vidner.net
|
73
|
+
executables:
|
74
|
+
- ytrello
|
75
|
+
- ytrello-addurl
|
76
|
+
- ytrello-check
|
77
|
+
- ytrello-create
|
78
|
+
- ytrello-reassign
|
79
|
+
extensions: []
|
80
|
+
extra_rdoc_files: []
|
81
|
+
files:
|
82
|
+
- ".gitignore"
|
83
|
+
- ".rubocop.yml"
|
84
|
+
- ".travis.yml"
|
85
|
+
- Gemfile
|
86
|
+
- Gemfile.lock
|
87
|
+
- README.md
|
88
|
+
- VERSION
|
89
|
+
- bin/ytrello
|
90
|
+
- bin/ytrello-addurl
|
91
|
+
- bin/ytrello-check
|
92
|
+
- bin/ytrello-create
|
93
|
+
- bin/ytrello-reassign
|
94
|
+
- config/trello-creds.yml.template
|
95
|
+
- devel/show_list_ids
|
96
|
+
- lib/ytrello.rb
|
97
|
+
- lib/ytrello/version.rb
|
98
|
+
- ytrello.gemspec
|
99
|
+
homepage: https://github.com/mvidner/ytrello
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
metadata: {}
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements:
|
118
|
+
- python-bugzilla, with the SUSE flavor
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 2.2.5
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: Tools to help with the YaST Trello boards and Bugzilla
|
124
|
+
test_files: []
|