bb-flow 0.1.4 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1a0d4d74ad7aa4c5f5145d51260ef78a9ffaf99f
4
- data.tar.gz: 7f88dfb42644f2a4707ea43b93ef264739f2291e
3
+ metadata.gz: e3faa34540e4510abeb1c59b4952b9e469b3c6db
4
+ data.tar.gz: 9fb3053a6282421986a4b437f481b6263bb3346e
5
5
  SHA512:
6
- metadata.gz: c6e5ff479bc5a71682766b425d63bd5646a2f727078a2dd85a8d7d3d79b39cb7eeab6af60251fa21bddda1bc74e0d59b193a66ba0933a19e9905fc69e4ea1b9e
7
- data.tar.gz: 9ff36f8a05a65b275c6662e84def0acb9945cab21a940fb8db6ffbd5166650a920e723037a6aaa0d4b94ea7c7f8e0525c899c5f9f5c1d4555990a102fc1c97ce
6
+ metadata.gz: b4d781b7b3296dfcca657237d789d95fccb87ddd899f75d346bb28ea87efe562d068ace17e19097d2d5eab707e2f200099c3711e65ed14a06454ab81604ede46
7
+ data.tar.gz: a29747a78c33ff25bde7d52920e9422fba88b5d64ccac11fac2c58468307b513db4c3e34f369e16e9c956f9fadcf1c4b733d30d5d2b376d3bdefdf90ab1cb908
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ bb-flow
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ #### 0.3.0
2
+
3
+ * PR: Open in your browser after creating.
4
+ * PR: Allow to specify the base branch (useful when you need to merge into a release branch).
5
+ * PR: Ask whether to move the cards if merging into a non-default branch.
6
+ * PR: Add the Trello icon in front of card links.
7
+ * PR: Sort labels alphabetically.
8
+ * PR: Guess which labels to apply.
9
+ * Align card titles after the list of [MODULES].
10
+
11
+ #### 0.2.0
12
+
13
+ * PR: Add GitHub pull requests via the Trello PowerUp
14
+ * Commit: Add commits via the Trello PowerUp
15
+
16
+ #### 0.1.5
17
+
18
+ * PR: Add a possibility to attach labels
19
+
1
20
  #### 0.1.4
2
21
 
3
22
  * Check for updates on start.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bb-flow (0.1.4)
4
+ bb-flow (0.3.0)
5
5
  ansi-select (= 0.3.4)
6
6
  colorize (~> 0.7.7)
7
7
  git (~> 1.2.9.1)
@@ -12,10 +12,10 @@ PATH
12
12
  GEM
13
13
  remote: https://rubygems.org/
14
14
  specs:
15
- activemodel (4.2.4)
16
- activesupport (= 4.2.4)
15
+ activemodel (4.2.5)
16
+ activesupport (= 4.2.5)
17
17
  builder (~> 3.1)
18
- activesupport (4.2.4)
18
+ activesupport (4.2.5)
19
19
  i18n (~> 0.7)
20
20
  json (~> 1.7, >= 1.7.7)
21
21
  minitest (~> 5.1)
@@ -40,10 +40,10 @@ GEM
40
40
  json (1.8.3)
41
41
  memoist (0.12.0)
42
42
  method_source (0.8.2)
43
- mime-types (2.6.2)
44
- minitest (5.8.1)
43
+ mime-types (2.99)
44
+ minitest (5.8.3)
45
45
  multipart-post (2.0.0)
46
- netrc (0.10.3)
46
+ netrc (0.11.0)
47
47
  oauth (0.4.7)
48
48
  octokit (4.1.1)
49
49
  sawyer (~> 0.6.0, >= 0.5.3)
@@ -89,10 +89,10 @@ PLATFORMS
89
89
 
90
90
  DEPENDENCIES
91
91
  bb-flow!
92
- bundler (~> 1.10.6)
92
+ bundler (~> 1.11)
93
93
  pry (~> 0.10.2)
94
94
  rake (~> 10.4.2)
95
95
  rubocop (~> 0.34.2)
96
96
 
97
97
  BUNDLED WITH
98
- 1.10.6
98
+ 1.11.2
data/bb-flow.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_dependency 'colorize', '~> 0.7.7'
25
25
  spec.add_dependency 'octokit', '~> 4.1.1'
26
26
 
27
- spec.add_development_dependency 'bundler', '~> 1.10.6'
27
+ spec.add_development_dependency 'bundler', '~> 1.11'
28
28
  spec.add_development_dependency 'pry', '~> 0.10.2'
29
29
  spec.add_development_dependency 'rake', '~> 10.4.2'
30
30
  spec.add_development_dependency 'rubocop', '~> 0.34.2'
data/bin/bb-flow CHANGED
@@ -13,8 +13,6 @@ Commands are:
13
13
  See 'bb-flow COMMAND --help' for more information on a specific command.
14
14
  HELP
15
15
 
16
- options = {}
17
-
18
16
  global = OptionParser.new do |opts|
19
17
  opts.banner = 'Usage: bb-flow [options] [command [options]]'
20
18
  opts.separator ''
@@ -33,33 +31,26 @@ end
33
31
  subcommands = {
34
32
  commit: OptionParser.new do |opts|
35
33
  opts.banner = 'Usage: commit [options]'
36
- opts.on('-m', '--message MESSAGE', 'Commit message') do |m|
37
- options[:message] = m
34
+ opts.on('-m', '--message MESSAGE', 'Commit message') do |commit_message|
35
+ BBFlow::Options.set(:commit_message, commit_message)
38
36
  end
39
37
  end,
40
38
  pr: OptionParser.new do |opts|
41
39
  opts.banner = 'Usage: pr'
40
+ opts.on('-b', '--base-branch BRANCH_NAME', 'The branch to merge into. DEFAULT: master') do |base_branch|
41
+ BBFlow::Options.set(:base_branch, base_branch)
42
+ end
42
43
  end
43
44
  }
44
45
 
45
46
  begin
46
47
  global.order!
47
- options[:command] = command = (ARGV.shift || '').to_sym
48
- mandatory = {
49
- commit: [],
50
- pr: []
51
- }
52
- if !command || !mandatory[command]
48
+ command = (ARGV.shift || '').to_sym
49
+ if command.blank?
53
50
  puts global
54
51
  exit
55
52
  end
56
53
  subcommands[command].order! if subcommands[command]
57
- missing = mandatory[command].select { |param| options[param].nil? }
58
- unless missing.empty?
59
- puts "Missing options: #{missing.join(', ')}"
60
- puts subcommands[command]
61
- exit
62
- end
63
54
  rescue OptionParser::InvalidOption, OptionParser::MissingArgument => exception
64
55
  puts exception.message
65
56
  exit(1)
@@ -69,4 +60,4 @@ if Gem::Platform.local.os == 'darwin' && !File.exist?(BBFlow::Persistent::Store.
69
60
  Thread.new { `say "Hi. It seems to be your first time running bb-flow. I hope you will like it. By the way, in this session I'll ask you more questions that usual: don't be scared, next time it will be a lot simpler."` }
70
61
  end
71
62
 
72
- BBFlow::Application.run(options)
63
+ BBFlow::Application.run(command)
data/lib/bb_flow.rb CHANGED
@@ -20,18 +20,19 @@ require_relative 'bb_flow/pull_requester'
20
20
  require_relative 'bb_flow/trello'
21
21
  require_relative 'bb_flow/github'
22
22
  require_relative 'bb_flow/updater'
23
+ require_relative 'bb_flow/options'
23
24
 
24
25
  module BBFlow
25
26
  class Application
26
- # @param [Hash] options
27
+ # @param [Symbol] command
27
28
  #
28
29
  # @return [void]
29
- def self.run(options)
30
- case options[:command]
30
+ def self.run(command)
31
+ case command
31
32
  when :commit
32
33
  return puts Misc.execute('git commit') if Misc.execute('git diff-index HEAD --name-only --cached').empty?
33
34
 
34
- message = options[:message] || Misc.edit
35
+ message = (Options.get(:message) || Misc.edit).strip
35
36
  cards = Trello.cards
36
37
  cards = Printer.select_items(cards, 'Which cards does this commit address?') unless cards.blank?
37
38
  card_lines = cards.map do |card|
@@ -40,18 +41,18 @@ module BBFlow
40
41
  # TODO: re-write with plumbing commands
41
42
  # e.g. git write-tree | xargs git commit-tree -m'message'
42
43
  Misc.execute("git commit -m \"#{message}\n\n#{card_lines}\"")
43
- commit_sha = Misc.execute('git show-ref --head -s /HEAD/')
44
+ commit_sha = Misc.execute('git show-ref --head -s /HEAD/').strip
44
45
  # TODO: convert to http url.
45
46
  commit_url = "#{Github.http_url}/commit/#{commit_sha}"
46
47
 
47
48
  cards.each do |card|
48
- card.add_comment("Commit: #{options[:message]}\n#{commit_url}")
49
- Printer.success("Added a comment to the „#{card.name}“ card (#{card.url}).")
49
+ card.add_attachment(commit_url, message)
50
+ Printer.success("Added the commit to the „#{card.name}“ card (#{card.url}).")
50
51
  end
51
52
  when :pr
52
53
  PullRequester.create!
53
54
  else
54
- Printer.panic("Incorrect parameter: #{options[:command]}")
55
+ Printer.panic("Incorrect parameter: #{command}")
55
56
  end
56
57
  end
57
58
  end
@@ -18,12 +18,12 @@ module BBFlow
18
18
 
19
19
  # @return [Array<Sawyer::Resource>]
20
20
  memoize def commits_info
21
- octokit.compare(repository_name, 'master', local_branch).commits
21
+ octokit.compare(repository_name, Options.get(:base_branch), local_branch).commits
22
22
  end
23
23
 
24
24
  # People who worked with you on this branch.
25
25
  #
26
- # @return [Array<String]
26
+ # @return [Array<String>]
27
27
  memoize def other_branch_committers
28
28
  (commits_info.map(&:author) + commits_info.map(&:committer)).map(&:login).uniq - [octokit.user.login]
29
29
  end
@@ -39,7 +39,7 @@ module BBFlow
39
39
  # @return [Sawyer::Resource] the newly created pull request.
40
40
  def create_pull_request(title, body)
41
41
  puts 'Creating a pull request...'
42
- octokit.create_pull_request(repository_name, 'master', local_branch, title, body)
42
+ octokit.create_pull_request(repository_name, Options.get(:base_branch), local_branch, title, body)
43
43
  end
44
44
 
45
45
  # @return [Array<Array<String>>] Array<[login, name]>
@@ -48,6 +48,21 @@ module BBFlow
48
48
  octokit.collaborators(repository_name).map(&:login).sort.map(&octokit.method(:user)).map { |user| [user.login, user.name] }
49
49
  end
50
50
 
51
+ # @return [Array<String>]
52
+ persistent local def labels
53
+ puts 'Fetching repository labels...'
54
+ octokit.labels(repository_name).map(&:name)
55
+ end
56
+
57
+ # @param [Sawyer::Resource] pull_request
58
+ # @param [Array<String>] labels
59
+ #
60
+ # @return [void]
61
+ def add_labels_to_pull_request(pull_request, labels)
62
+ puts 'Adding labels to pull request...'
63
+ octokit.add_labels_to_an_issue(repository_name, pull_request, labels)
64
+ end
65
+
51
66
  # @return [Boolean]
52
67
  def has_branch?
53
68
  Misc.git.is_remote_branch?(local_branch)
@@ -64,6 +79,10 @@ module BBFlow
64
79
  Misc.execute(git_command).strip.to_i
65
80
  end
66
81
 
82
+ memoize def changed_files
83
+ Misc.execute("git diff --name-only #{Options.get(:base_branch)} #{remote_branch}").split
84
+ end
85
+
67
86
  # @return [String]
68
87
  def remote_name
69
88
  remote.name
data/lib/bb_flow/misc.rb CHANGED
@@ -5,6 +5,19 @@ module BBFlow
5
5
  class << self
6
6
  extend Memoist
7
7
 
8
+ # @param [String] url
9
+ #
10
+ # @return [void]
11
+ def open_url(url)
12
+ if Gem::Platform.local.os == 'darwin'
13
+ execute("open #{url}")
14
+ else
15
+ # FIXME: I'm not sure what's the proper way to open URLs on either Linux
16
+ # or Windows. If it's important for you, don't hesitate to edit.
17
+ puts(url)
18
+ end
19
+ end
20
+
8
21
  # @param [Array<Object>] args
9
22
  #
10
23
  # @return [String]
@@ -39,6 +52,14 @@ module BBFlow
39
52
  memoize def editor
40
53
  [`git config --global core.editor`.chomp.split(/\s+/), ['vi']].reject(&:empty?).first
41
54
  end
55
+
56
+ # @param [String] word
57
+ # @param [Fixnum] count
58
+ #
59
+ # @return [String]
60
+ def pluralize(word, count)
61
+ count == 1 ? word : word + 's'
62
+ end
42
63
  end
43
64
  end
44
65
  end
@@ -0,0 +1,44 @@
1
+
2
+ module BBFlow
3
+ class Options
4
+ class << self
5
+ ALLOWED_OPTIONS = {
6
+ base_branch: { default_value: 'master' },
7
+ commit_message: {}
8
+ }
9
+
10
+ # @param [Symbol] name
11
+ # @param [Object] value
12
+ #
13
+ # @raise [ArgumentError]
14
+ # @return [void]
15
+ def set(name, value)
16
+ raise ArgumentError "Trying to set an unknown option #{name}" unless ALLOWED_OPTIONS.keys.include?(name)
17
+
18
+ @storage ||= {}
19
+ @storage[name] = value
20
+ end
21
+
22
+ # @param [Symbol] name
23
+ #
24
+ # @raise [ArgumentError]
25
+ # @return [Object]
26
+ def get(name)
27
+ raise ArgumentError, "Trying to get an unknown option #{name}" unless ALLOWED_OPTIONS.keys.include?(name)
28
+
29
+ @storage ||= {}
30
+ @storage[name] || default(name)
31
+ end
32
+
33
+ # @param [Symbol] name
34
+ #
35
+ # @raise [ArgumentError]
36
+ # @return [Object]
37
+ def default(name)
38
+ raise ArgumentError, "Trying to get a default for an unknown option #{name}" unless ALLOWED_OPTIONS.keys.include?(name)
39
+
40
+ ALLOWED_OPTIONS[name][:default_value]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -19,18 +19,27 @@ module BBFlow
19
19
  else
20
20
  response = Github.create_pull_request(title, Misc.edit(body_template) + ADVERTISEMENT)
21
21
 
22
+ unless labels.empty?
23
+ Github.add_labels_to_pull_request(response.number, labels)
24
+ end
25
+
22
26
  unless cards.empty?
23
- add_link_to_cards(response.html_url)
24
- move_cards_to_done_list
27
+ add_pull_request_to_cards(response)
28
+
29
+ if Options.get(:base_branch) == Options.default(:base_branch) ||
30
+ Printer.simple_question("You're going to merge into a non-default branch. Would you still like me to move the #{Misc.pluralize('card', cards.size)}?")
31
+ move_cards_to_done_list
32
+ end
25
33
  end
26
34
 
27
35
  Printer.success('The pull request has been created, sir.')
28
- puts response.html_url
36
+ Misc.open_url(response.html_url)
29
37
  end
30
38
  rescue Octokit::Error => exception
31
39
  Printer.error(exception.message)
32
40
  end
33
41
 
42
+ # @return [void]
34
43
  def ensure_has_remote!
35
44
  unless Github.has_branch?
36
45
  question = "The branch isn't pushed yet. Would you like me to push it for you?"
@@ -45,6 +54,7 @@ module BBFlow
45
54
  end
46
55
  end
47
56
 
57
+ # @return [void]
48
58
  def ensure_pushed!
49
59
  unless Github.pushed?
50
60
  question = "Your local branch is ahead of '#{Github.remote_branch}' by #{Github.unpushed_commits_number} commits. Would you like me to push it?"
@@ -61,13 +71,15 @@ module BBFlow
61
71
  cards.each { |card| card.move_to_list(Trello.done_list) }
62
72
  end
63
73
 
64
- # @param [String] url
65
- def add_link_to_cards(url)
66
- puts 'Adding the pull request link to the trello cards...'
74
+ # @param [Sawyer::Resource] pull_request
75
+ def add_pull_request_to_cards(pull_request)
76
+ puts 'Adding the pull request to the trello cards...'
67
77
 
68
- cards.reject { |card| card.desc.include?(url) }.each do |card|
69
- card.desc = "[Pull Request](#{url})\n\n#{card.desc}"
70
- card.save
78
+ cards.reject { |card| card.attachments.map(&:url).include?(pull_request.html_url) }.each do |card|
79
+ # Note that this title is formatted in a special way that makes Trello recognize it as
80
+ # a GitHub PR instead of a regular attachment.
81
+ title = "#{pull_request.head.repo.name} ##{pull_request.number} #{pull_request.title}"
82
+ card.add_attachment(pull_request.html_url, title)
71
83
  end
72
84
  end
73
85
 
@@ -85,13 +97,12 @@ module BBFlow
85
97
  # @return [String]
86
98
  def body_template
87
99
  reviewers_md = reviewers.map { |login, _name| "- [ ] @#{login}" }
88
- card_links = cards.map { |card| " [#{card.name}](#{card.url})" }
100
+ card_links = cards.map { |card| %Q(<a href="#{card.url}"><img src="https://github.trello.services/images/trello-icon.png" width="12" height="12"> #{card.name}</a>) }
89
101
 
90
102
  body_parts = []
91
103
  body_parts << reviewers_md.join("\n") unless reviewers_md.empty?
92
104
 
93
105
  unless card_links.empty?
94
- body_parts << '###### Trello'
95
106
  body_parts << card_links.join("\n")
96
107
  end
97
108
 
@@ -120,6 +131,19 @@ module BBFlow
120
131
  Persistent::Store.get(:local).transaction { |store| store['previous_reviewers'] = reviewers }
121
132
  end
122
133
 
134
+ # @return [Array<String>]
135
+ memoize def labels
136
+ labels = Github.labels.sort_by(&:downcase)
137
+ downcased = labels.map(&:downcase)
138
+
139
+ Printer.select_items(
140
+ labels,
141
+ 'Which labels do you want to add to this pull request?',
142
+ formatter: ->(label) { label },
143
+ preselected: downcased.map(&guessed_labels(downcased).method(:include?))
144
+ )
145
+ end
146
+
123
147
  # @return [Array<Trello::Card>]
124
148
  memoize def cards
125
149
  return [] if Trello.cards.blank?
@@ -127,7 +151,7 @@ module BBFlow
127
151
  Printer.select_items(
128
152
  Trello.cards,
129
153
  'Which cards does it implement?',
130
- formatter: ->(card) { [card.name, card.short_url] },
154
+ formatter: method(:format_trello_title),
131
155
  preselected: Trello.cards.map { |card| commit_messages.any? { |message| message.include?(card.short_url) } }
132
156
  )
133
157
  end
@@ -136,6 +160,31 @@ module BBFlow
136
160
  memoize def commit_messages
137
161
  Github.commits_info.reject { |info| info.parents.size > 1 }.map { |info| info.commit.message }
138
162
  end
163
+
164
+ # @param [Array<String>] labels
165
+ #
166
+ # @return [Array<String>]
167
+ def guessed_labels(labels)
168
+ specialized_predicates = {
169
+ 'bugfix' => -> { Trello.labels.include?('bug') },
170
+ 'refactoring' => -> { Github.changed_files.size > 50 || commit_messages.map(&:downcase).any? { |message| message.include?('refactor') } },
171
+ 'specs' => -> { Github.changed_files.present? && Github.changed_files.all? { |file| file.start_with?('spec/') && file.end_with?('_spec.rb') } },
172
+ 'wip' => -> { %w(wip skip-ci).any?(&commit_messages.last.downcase.method(:include?)) },
173
+ 'ui' => -> { Github.changed_files.present? && Github.changed_files.all? { |file| file.start_with?('app/assets/stylesheets/') } }
174
+ }.tap { |hash| hash.default = ->() { false } }
175
+
176
+ labels.select { |label| Misc.git.current_branch.include?(label) || Trello.labels.include?(label) || specialized_predicates[label].call }
177
+ end
178
+
179
+ # @param [Trello::Card] card
180
+ #
181
+ # @return [Array<String>]
182
+ def format_trello_title(card)
183
+ prefix, _, title = card.name.rpartition(/(?<=\])\s*/) # At last closing square bracket.
184
+ _score, _, tags = prefix.rpartition(/(?<=\))\s*/) # After last parentheses.
185
+
186
+ [tags, title, card.short_url]
187
+ end
139
188
  end
140
189
  end
141
190
  end
@@ -10,6 +10,11 @@ module BBFlow
10
10
  lists.flat_map { |list| list.cards.target }.select { |card| card.member_ids.include?(member.id) }
11
11
  end
12
12
 
13
+ # @return [Array<String>]
14
+ memoize def labels
15
+ cards.flat_map { |card| card.labels.target }.map(&:name).map(&:downcase)
16
+ end
17
+
13
18
  # @return [Trello::List]
14
19
  memoize def done_list
15
20
  trello::List.find(done_list_id)
@@ -5,11 +5,11 @@ module BBFlow
5
5
 
6
6
  # Current minor release.
7
7
  # @return [String]
8
- MINOR = 1
8
+ MINOR = 3
9
9
 
10
10
  # Current patch level.
11
11
  # @return [String]
12
- PATCH = 4
12
+ PATCH = 0
13
13
 
14
14
  # Full release version.
15
15
  # @return [String]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bb-flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gregolsen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-25 00:00:00.000000000 Z
11
+ date: 2016-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ansi-select
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 1.10.6
103
+ version: '1.11'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 1.10.6
110
+ version: '1.11'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: pry
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -162,6 +162,8 @@ files:
162
162
  - ".gitignore"
163
163
  - ".rubocop.yml"
164
164
  - ".rubocop_todo.yml"
165
+ - ".ruby-gemset"
166
+ - ".ruby-version"
165
167
  - CHANGELOG.md
166
168
  - Gemfile
167
169
  - Gemfile.lock
@@ -174,6 +176,7 @@ files:
174
176
  - lib/bb_flow.rb
175
177
  - lib/bb_flow/github.rb
176
178
  - lib/bb_flow/misc.rb
179
+ - lib/bb_flow/options.rb
177
180
  - lib/bb_flow/persistent.rb
178
181
  - lib/bb_flow/printer.rb
179
182
  - lib/bb_flow/pull_requester.rb
@@ -200,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
203
  version: '0'
201
204
  requirements: []
202
205
  rubyforge_project:
203
- rubygems_version: 2.4.8
206
+ rubygems_version: 2.2.2
204
207
  signing_key:
205
208
  specification_version: 4
206
209
  summary: Simplifies typical BrightBytes work flows