ytrello 0.18.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 +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: []
|