bb-flow 0.1.4 → 0.3.0

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