bb-flow 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4aa0d7d44a2b105073dccd71ae17aa7276447fef
4
+ data.tar.gz: 68f380edd8dd3a5dbd7da6666f5e172048bc4f0f
5
+ SHA512:
6
+ metadata.gz: d0cccdd262c828455900d03245a5693037aef291d5e74567fd7651fead5ce62f00ab56b2a845c9c0924d941be25ea3ea0ff616a9068cfeca94630021a499ca70
7
+ data.tar.gz: 880d912266652986830fe373f44150289f23902649bf93dd4ffd4c4627f4f378b81f774263e76f3b12c2e48446743df1d5213b4241930adfdaee5121a96fa0a9
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,15 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Metrics/ClassLength:
4
+ Max: 200
5
+
6
+ Metrics/LineLength:
7
+ Max: 120
8
+
9
+ Style/Documentation:
10
+ Enabled: false
11
+
12
+ Style/FileName:
13
+ Exclude:
14
+ - 'bin/bb-flow'
15
+ - 'lib/bb-flow.rb'
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,22 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2015-10-08 19:43:18 +0300 using RuboCop version 0.34.2.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ # Cop supports --auto-correct.
11
+ # Configuration parameters: AlignWith, SupportedStyles, AutoCorrect.
12
+ Lint/EndAlignment:
13
+ Enabled: false
14
+
15
+ # Offense count: 4
16
+ Metrics/AbcSize:
17
+ Max: 35
18
+
19
+ # Offense count: 4
20
+ # Configuration parameters: CountComments.
21
+ Metrics/MethodLength:
22
+ Max: 25
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bb_flow.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,98 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bb-flow (0.1.0)
5
+ ansi-select (~> 0.2.5)
6
+ colorize (~> 0.7.7)
7
+ git (~> 1.2.9.1)
8
+ memoist (~> 0.12.0)
9
+ octokit (~> 4.1.1)
10
+ ruby-trello (~> 1.3.0)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ activemodel (4.2.4)
16
+ activesupport (= 4.2.4)
17
+ builder (~> 3.1)
18
+ activesupport (4.2.4)
19
+ i18n (~> 0.7)
20
+ json (~> 1.7, >= 1.7.7)
21
+ minitest (~> 5.1)
22
+ thread_safe (~> 0.3, >= 0.3.4)
23
+ tzinfo (~> 1.1)
24
+ addressable (2.3.8)
25
+ ansi-select (0.2.5)
26
+ ast (2.1.0)
27
+ astrolabe (1.3.1)
28
+ parser (~> 2.2)
29
+ builder (3.2.2)
30
+ coderay (1.1.0)
31
+ colorize (0.7.7)
32
+ domain_name (0.5.25)
33
+ unf (>= 0.0.5, < 1.0.0)
34
+ faraday (0.9.2)
35
+ multipart-post (>= 1.2, < 3)
36
+ git (1.2.9.1)
37
+ http-cookie (1.0.2)
38
+ domain_name (~> 0.5)
39
+ i18n (0.7.0)
40
+ json (1.8.3)
41
+ memoist (0.12.0)
42
+ method_source (0.8.2)
43
+ mime-types (2.6.2)
44
+ minitest (5.8.1)
45
+ multipart-post (2.0.0)
46
+ netrc (0.10.3)
47
+ oauth (0.4.7)
48
+ octokit (4.1.1)
49
+ sawyer (~> 0.6.0, >= 0.5.3)
50
+ parser (2.2.3.0)
51
+ ast (>= 1.1, < 3.0)
52
+ powerpack (0.1.1)
53
+ pry (0.10.2)
54
+ coderay (~> 1.1.0)
55
+ method_source (~> 0.8.1)
56
+ slop (~> 3.4)
57
+ rainbow (2.0.0)
58
+ rake (10.4.2)
59
+ rest-client (1.8.0)
60
+ http-cookie (>= 1.0.2, < 2.0)
61
+ mime-types (>= 1.16, < 3.0)
62
+ netrc (~> 0.7)
63
+ rubocop (0.34.2)
64
+ astrolabe (~> 1.3)
65
+ parser (>= 2.2.2.5, < 3.0)
66
+ powerpack (~> 0.1)
67
+ rainbow (>= 1.99.1, < 3.0)
68
+ ruby-progressbar (~> 1.4)
69
+ ruby-progressbar (1.7.5)
70
+ ruby-trello (1.3.0)
71
+ activemodel (>= 3.2.0)
72
+ addressable (~> 2.3)
73
+ json
74
+ oauth (~> 0.4.5)
75
+ rest-client (~> 1.8.0)
76
+ sawyer (0.6.0)
77
+ addressable (~> 2.3.5)
78
+ faraday (~> 0.8, < 0.10)
79
+ slop (3.6.0)
80
+ thread_safe (0.3.5)
81
+ tzinfo (1.2.2)
82
+ thread_safe (~> 0.1)
83
+ unf (0.1.4)
84
+ unf_ext
85
+ unf_ext (0.0.7.1)
86
+
87
+ PLATFORMS
88
+ ruby
89
+
90
+ DEPENDENCIES
91
+ bb-flow!
92
+ bundler (~> 1.10.6)
93
+ pry (~> 0.10.2)
94
+ rake (~> 10.4.2)
95
+ rubocop (~> 0.34.2)
96
+
97
+ BUNDLED WITH
98
+ 1.10.6
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ A command line utility, which was born to simplify routines required by the BrightBites workflow.
2
+
3
+ ## Installation
4
+
5
+
6
+ git clone git@github.com:brightbytes/bb-flow.git
7
+ cd bb-flow
8
+ # At this point choose the correct ruby version and gemset to install to, if you use RVM.
9
+ rake install
10
+
11
+ ## Usage
12
+
13
+ Currently we support two commands:
14
+
15
+ bb-flow commit -m 'Commit message' # Create a commit.
16
+ git pr # Create a pull request.
17
+
18
+ ## Roadmap
19
+ [Roadmap google doc](https://docs.google.com/document/d/1voXHU5H-M3YEwKKp1bGeYxGALNc6GB91fJ7eBfOMZ4k/edit)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/bb-flow.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bb_flow/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'bb-flow'
8
+ spec.version = BBFlow::VERSION
9
+ spec.authors = ['gregolsen']
10
+ spec.email = ['mihailov.in@gmail.com']
11
+ spec.summary = 'Simplifies typical BrightBytes work flows'
12
+ spec.homepage = ''
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_dependency 'ansi-select', '~> 0.2.5'
21
+ spec.add_dependency 'git', '~> 1.2.9.1'
22
+ spec.add_dependency 'memoist', '~> 0.12.0'
23
+ spec.add_dependency 'ruby-trello', '~> 1.3.0'
24
+ spec.add_dependency 'colorize', '~> 0.7.7'
25
+ spec.add_dependency 'octokit', '~> 4.1.1'
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.10.6'
28
+ spec.add_development_dependency 'pry', '~> 0.10.2'
29
+ spec.add_development_dependency 'rake', '~> 10.4.2'
30
+ spec.add_development_dependency 'rubocop', '~> 0.34.2'
31
+ end
data/bin/bb-flow ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/bb-flow'
4
+ require 'optparse'
5
+
6
+ subtext = <<HELP
7
+ Commands are:
8
+ commit : Commit changes and add comment to Trello cards
9
+ pr : Create a pull request
10
+
11
+ See 'bb-flow COMMAND --help' for more information on a specific command.
12
+ HELP
13
+
14
+ options = {}
15
+
16
+ global = OptionParser.new do |opts|
17
+ opts.banner = 'Usage: bb-flow [options] [command [options]]'
18
+ opts.separator ''
19
+ opts.separator subtext
20
+ opts.on_tail('-h', '--help', 'Show this message') do
21
+ puts opts
22
+ exit
23
+ end
24
+ end
25
+
26
+ subcommands = {
27
+ commit: OptionParser.new do |opts|
28
+ opts.banner = 'Usage: commit [options]'
29
+ opts.on('-m', '--message MESSAGE', 'Commit message') do |m|
30
+ options[:message] = m
31
+ end
32
+ end,
33
+ pr: OptionParser.new do |opts|
34
+ opts.banner = 'Usage: pr'
35
+ end
36
+ }
37
+
38
+ begin
39
+ global.order!
40
+ options[:command] = command = (ARGV.shift || '').to_sym
41
+ mandatory = {
42
+ commit: [],
43
+ pr: []
44
+ }
45
+ if !command || !mandatory[command]
46
+ puts global
47
+ exit
48
+ end
49
+ subcommands[command].order! if subcommands[command]
50
+ missing = mandatory[command].select { |param| options[param].nil? }
51
+ unless missing.empty?
52
+ puts "Missing options: #{missing.join(', ')}"
53
+ puts subcommands[command]
54
+ exit
55
+ end
56
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument => exception
57
+ puts exception.message
58
+ exit(1)
59
+ end
60
+
61
+ if Gem::Platform.local.os == 'darwin' && !File.exist?(BBFlow::Persistent::Store.location(:global))
62
+ 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."` }
63
+ end
64
+
65
+ BBFlow::Application.run(options)
data/bin/git-pr ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/bb-flow'
4
+
5
+ BBFlow::Application.run(command: :pr)
data/lib/bb-flow.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative './bb_flow'
@@ -0,0 +1,91 @@
1
+ require 'octokit'
2
+
3
+ module BBFlow
4
+ class Github
5
+ class << self
6
+ extend BBFlow::Persistent
7
+
8
+ # @return [String]
9
+ def http_url
10
+ "https://github.com/#{repository_name}"
11
+ end
12
+
13
+ # @return [String]
14
+ persistent local def repository_url
15
+ remote.url
16
+ end
17
+
18
+ # @return [Sawyer::Resource]
19
+ def find_pull_request
20
+ octokit.pull_requests(repository_name).detect { |pr| pr.head.ref == branch_name }
21
+ end
22
+
23
+ # @param [String] title
24
+ # @param [String] body
25
+ #
26
+ # @return [Sawyer::Resource] the newly created pull request.
27
+ def create_pull_request(title, body)
28
+ puts 'Creating a pull request...'
29
+ octokit.create_pull_request(repository_name, 'master', branch_name, title, body)
30
+ end
31
+
32
+ # @return [Array<Array<String>>] Array<[login, name]>
33
+ persistent local def collaborators
34
+ octokit.collaborators(repository_name).map(&:login).sort.map(&octokit.method(:user)).map { |user| [user.login, user.name] }
35
+ end
36
+
37
+ # @return [Boolean]
38
+ def pushed?
39
+ branch_name
40
+ end
41
+
42
+ # @return [String]
43
+ def remote_name
44
+ remote.name
45
+ end
46
+
47
+ private
48
+
49
+ # @return [Octokit::Client]
50
+ def octokit
51
+ @octokit ||= Octokit::Client.new(access_token: token)
52
+ end
53
+
54
+ # @return [String]
55
+ def repository_name
56
+ # Match both git@github.com:shockone/bb-flow-test.git and https://github.com/shockone/bb-flow-test
57
+ repository_url[%r{github\.com[/:]([\w-]+/[\w-]+)(\.git|$)}, 1]
58
+ end
59
+
60
+ # @return [Git::Remote]
61
+ def remote
62
+ remotes = Misc.git.remotes.select { |remote| remote.url.include?('github') }
63
+
64
+ case remotes.size
65
+ when 0
66
+ Printer.panic("You don't have any remotes set up.")
67
+ when 1
68
+ remotes.first
69
+ else
70
+ Printer.select_item(
71
+ remotes,
72
+ 'Which remote would you like to push to?',
73
+ formatter: ->(remote) { [remote.name, remote.url] }
74
+ )
75
+ end
76
+ end
77
+
78
+ # @return [String]
79
+ def branch_name
80
+ rev = Misc.execute('git rev-parse --abbrev-ref --symbolic-full-name @{u}').strip
81
+ %r{(.*?)/(.*)}.match(rev)[2] unless rev.empty?
82
+ end
83
+
84
+ # @return [String]
85
+ persistent global def token
86
+ url = 'https://github.com/settings/tokens/new'
87
+ Printer.ask("Paste a Github token. You can generate it on #{url} (leave default scopes): ")
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,44 @@
1
+ require 'git'
2
+
3
+ module BBFlow
4
+ class Misc
5
+ class << self
6
+ extend Memoist
7
+
8
+ # @param [Array<Object>] args
9
+ #
10
+ # @return [String]
11
+ def execute(*args)
12
+ Open3.popen3(*args) { |_i, o| return o.read }
13
+ end
14
+
15
+ # @param [String] text
16
+ #
17
+ # @return [String]
18
+ def edit(text = '')
19
+ file = Tempfile.new(%w(bb-flow .md))
20
+ file.write(text)
21
+ file.flush
22
+
23
+ system(*[*Misc.editor, file.path])
24
+
25
+ File.read(file.path)
26
+ ensure
27
+ file.close
28
+ file.unlink
29
+ end
30
+
31
+ # @return [Git::Base]
32
+ memoize def git
33
+ Git.open(Dir.pwd)
34
+ rescue ArgumentError
35
+ Printer.panic("This directory (#{__dir__}) is not a git repository.")
36
+ end
37
+
38
+ # @return [Array<String>] editor with arguments.
39
+ memoize def editor
40
+ [`git config --global core.editor`.chomp.split(/\s+/), ['vi']].reject(&:empty?).first
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,78 @@
1
+ require 'yaml/store'
2
+
3
+ module BBFlow
4
+ # A bit of meta programming that allows to replace
5
+ #
6
+ # def trello_key
7
+ # @trello_key ||= Config.global.transaction do |store|
8
+ # store['trello'] ||= {}
9
+ # store['trello']['key'] ||= Printer.ask('Go to https://trello.com/app-key and paste Key: ')
10
+ # end
11
+ # end
12
+ #
13
+ # with
14
+ #
15
+ # class Trello
16
+ # extend Persistent
17
+ #
18
+ # persistent global def key
19
+ # Printer.ask('Go to https://trello.com/app-key and paste Key: ')
20
+ # end
21
+ # end
22
+ #
23
+ # The method with be memoized both within and across executions.
24
+ module Persistent
25
+ # @param [Array<Config, Symbol>] args
26
+ def persistent(args)
27
+ store, method_name = args
28
+
29
+ module_with_persistent_method = Module.new do
30
+ define_method(method_name) do
31
+ prefix = name.gsub(/^.*::/, '').downcase
32
+ instance_variable_name = "@#{method_name}_memo"
33
+
34
+ accessor = proc do |hash|
35
+ hash[prefix] ||= {}
36
+ hash[prefix][method_name.to_s] ||= super()
37
+ end
38
+
39
+ instance_variable_get(instance_variable_name) ||
40
+ instance_variable_set(instance_variable_name, (store.instance_variable_get('@lock').locked? ? accessor.call(store) : store.transaction(&accessor)))
41
+ end
42
+ end
43
+
44
+ prepend module_with_persistent_method
45
+ end
46
+
47
+ # @!method global(method_name)
48
+ # @!method local(method_name)
49
+ # @param [Symbol] method_name
50
+ # @note intended to be used with #persistent
51
+ #
52
+ # @return [Array<Config, Symbol>]
53
+ %i(global local).each do |store_helper|
54
+ define_method(store_helper) do |method_name|
55
+ [Store.get(store_helper), method_name]
56
+ end
57
+ end
58
+
59
+ class Store
60
+ FILE_NAME = '.bb_flow'
61
+ DIRS = { global: ENV['HOME'], local: Dir.pwd }
62
+
63
+ # @param [Symbol] name
64
+ #
65
+ # @return [YAML::Store]
66
+ def self.get(name)
67
+ @memo ||= {}
68
+ @memo[name] ||= YAML::Store.new(location(name))
69
+ end
70
+
71
+ def self.location(storage_type)
72
+ fail "Unknown store: #{name}" unless DIRS.keys.include?(storage_type)
73
+
74
+ File.join(DIRS[storage_type], FILE_NAME)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,74 @@
1
+ module BBFlow
2
+ class Printer
3
+ class << self
4
+ # @param [String] question
5
+ #
6
+ # @return [String] answer
7
+ def ask(question)
8
+ puts
9
+ note(question)
10
+ print('> ')
11
+ gets.strip
12
+ end
13
+
14
+ # @param [String] message
15
+ #
16
+ # @return [void]
17
+ def panic(message)
18
+ error(message)
19
+ abort
20
+ end
21
+
22
+ # @param [String] message
23
+ #
24
+ # @return [void]
25
+ def error(message)
26
+ puts("[ERROR] #{message}".colorize(:red))
27
+ end
28
+
29
+ # @param [String] message
30
+ #
31
+ # @return [void]
32
+ def success(message)
33
+ puts(message.colorize(:green))
34
+ end
35
+
36
+ # @param [String] message
37
+ #
38
+ # @return [void]
39
+ def note(message)
40
+ puts(message.colorize(:yellow))
41
+ end
42
+
43
+ # @param [String] message
44
+ #
45
+ # @return [void]
46
+ def auxiliary(message)
47
+ puts(message.colorize(:light_black))
48
+ end
49
+
50
+ # @param [Array<#name>] items
51
+ # @param [String] title
52
+ #
53
+ # @return [#name]
54
+ def select_item(items, title, formatter: :name.to_proc)
55
+ puts
56
+ note(title)
57
+ auxiliary('Move: arrows or j/k Finish: return')
58
+ Ansi::Selector.select(items, formatter: formatter)
59
+ end
60
+
61
+ # @param [Array<#name>] items
62
+ # @param [String] title
63
+ #
64
+ # @return [Array<#name>]
65
+ def select_items(items, title, formatter: :name.to_proc, preselected: [])
66
+ puts
67
+ note(title)
68
+ # TODO: Remove after implementing it in ansi-select.
69
+ auxiliary('Move: arrows or j/k Toggle: space Finish: return')
70
+ Ansi::Selector.multi_select(items, formatter: formatter, preselected: preselected)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,131 @@
1
+ require 'tempfile'
2
+
3
+ module BBFlow
4
+ class PullRequester
5
+ class << self
6
+ extend Memoist
7
+
8
+ AD = "\n\n<sub>This pull request has been created using [bb-flow](https://github.com/brightbytes/bb-flow).</sub>"
9
+
10
+ # @return [void]
11
+ def create!
12
+ ensure_pushed!
13
+
14
+ existing_pull_request = Github.find_pull_request
15
+
16
+ if existing_pull_request
17
+ Printer.success("A pull request for this branch already exists.\n#{existing_pull_request.html_url}")
18
+ else
19
+ response = Github.create_pull_request(title, Misc.edit(body_template) + AD)
20
+
21
+ unless cards.empty?
22
+ add_link_to_cards(response.html_url)
23
+ move_cards_to_done_list
24
+ end
25
+
26
+ Printer.success('The pull request has been created, sir.')
27
+ puts response.html_url
28
+ end
29
+ rescue Octokit::Error => exception
30
+ Printer.error(exception.message)
31
+ end
32
+
33
+ def ensure_pushed!
34
+ unless Github.pushed?
35
+ yes = 'Yes, please'
36
+
37
+ case Printer.select_item(
38
+ [yes, 'No, thank you'],
39
+ "The branch isn't pushed yet. Would you like me to push it for you?",
40
+ formatter: :to_s.to_proc
41
+ )
42
+ when yes
43
+ Misc.execute("git push --set-upstream #{Github.remote_name} #{Misc.git.current_branch}")
44
+ else
45
+ puts 'Exiting...'
46
+ exit
47
+ end
48
+ end
49
+ end
50
+
51
+ # @return [void]
52
+ def move_cards_to_done_list
53
+ puts "Moving cards to the „#{Trello.done_list.name}“ list..."
54
+ cards.each { |card| card.move_to_list(Trello.done_list) }
55
+ end
56
+
57
+ # @param [String] url
58
+ def add_link_to_cards(url)
59
+ puts 'Adding the pull request link to the trello cards...'
60
+
61
+ cards.each do |card|
62
+ next if card.desc.include?(url)
63
+
64
+ card.desc = "[Pull Request](#{url})\n\n#{card.desc}"
65
+ card.save
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # @return [String]
72
+ def title
73
+ if cards.size == 1
74
+ cards.first.name
75
+ else
76
+ Printer.ask('Title:')
77
+ end
78
+ end
79
+
80
+ # @return [String]
81
+ def body_template
82
+ reviewers_md = reviewers.map { |login, _name| "- [ ] @#{login}" }
83
+ card_links = cards.map { |card| " [#{card.name}](#{card.url})" }
84
+
85
+ body_parts = []
86
+ body_parts << reviewers_md.join("\n") unless reviewers_md.empty?
87
+
88
+ unless card_links.empty?
89
+ body_parts << '###### Trello'
90
+ body_parts << card_links.join("\n")
91
+ end
92
+
93
+ body_parts << '###### Changes'
94
+ body_parts << commits.map(&:message).map(&:lines).map(&:first).map { |message| " * #{message}" }.join
95
+
96
+ body_parts.join("\n\n")
97
+ end
98
+
99
+ # @return [Array<String>] [login, name]
100
+ def reviewers
101
+ previous_reviewers = Persistent::Store.get(:local).transaction(true) { |store| store['previous_reviewers'] } || []
102
+
103
+ reviewers = Printer.select_items(
104
+ Github.collaborators,
105
+ 'Who will be reviewing the pull request?',
106
+ formatter: proc { |login, name| [login, name] },
107
+ preselected: Github.collaborators.map { |user| previous_reviewers.include?(user) }
108
+ )
109
+
110
+ Persistent::Store.get(:local).transaction { |store| store['previous_reviewers'] = reviewers }
111
+ end
112
+
113
+ # @return [Array<Trello::Card>]
114
+ memoize def cards
115
+ return [] if Trello.cards.blank?
116
+
117
+ Printer.select_items(
118
+ Trello.cards,
119
+ 'Which cards does it implement?',
120
+ formatter: ->(card) { [card.name, card.short_url] },
121
+ preselected: Trello.cards.map { |card| commits.any? { |commit| commit.message.include?(card.short_url) } }
122
+ )
123
+ end
124
+
125
+ # @return [Array<Git::Object::Commit>]
126
+ memoize def commits
127
+ Misc.git.log.between('master', 'HEAD').to_a
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,72 @@
1
+ module BBFlow
2
+ class Trello
3
+ class << self
4
+ extend BBFlow::Persistent
5
+ extend Memoist
6
+
7
+ # @return [Array<Trello::Card>]
8
+ memoize def cards
9
+ puts 'Fetching Trello cards...'
10
+ lists.flat_map { |list| list.cards.target }.select { |card| card.member_ids.include?(member.id) }
11
+ end
12
+
13
+ # @return [Trello::List]
14
+ memoize def done_list
15
+ trello::List.find(done_list_id)
16
+ end
17
+
18
+ private
19
+
20
+ # @return [Trello::Board]
21
+ memoize def board
22
+ trello::Board.find(board_id)
23
+ end
24
+
25
+ # @return [Array<Trello::List>]
26
+ memoize def lists
27
+ list_ids.map { |list| trello::List.find(list) }
28
+ end
29
+
30
+ # @return [Trello::Member]
31
+ memoize def member
32
+ trello::Member.find('me')
33
+ end
34
+
35
+ # @return [Class]
36
+ memoize def trello
37
+ ::Trello.configure do |config|
38
+ config.developer_public_key = key
39
+ config.member_token = member_token
40
+ end
41
+
42
+ ::Trello
43
+ end
44
+
45
+ # @return [String]
46
+ persistent global def key
47
+ Printer.ask('Go to https://trello.com/app-key and paste Key: ')
48
+ end
49
+
50
+ # @return [String]
51
+ persistent global def member_token
52
+ authorization_url = "http://trello.com/1/authorize?key=#{key}&response_type=token&expiration=never&scope=read,write"
53
+ Printer.ask("Go to #{authorization_url}, click 'Allow', and paste Token")
54
+ end
55
+
56
+ # @return [String]
57
+ persistent local def board_id
58
+ Printer.select_item(trello::Board.all, 'Which Trello board do you work with?', formatter: ->(board) { [board.name, board.url] }).id
59
+ end
60
+
61
+ # @return [Array<String>]
62
+ persistent local def list_ids
63
+ Printer.select_items(board.lists, 'Which lists contain not finished - „in progress“ - cards?').map(&:id)
64
+ end
65
+
66
+ # @return [String]
67
+ persistent local def done_list_id
68
+ Printer.select_item(board.lists, 'Where should I move finished cards?').id
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,17 @@
1
+ module BBFlow
2
+ # Current major release.
3
+ # @return [String]
4
+ MAJOR = 0
5
+
6
+ # Current minor release.
7
+ # @return [String]
8
+ MINOR = 1
9
+
10
+ # Current patch level.
11
+ # @return [String]
12
+ PATCH = 0
13
+
14
+ # Full release version.
15
+ # @return [String]
16
+ VERSION = [MAJOR, MINOR, PATCH].join('.').freeze
17
+ end
data/lib/bb_flow.rb ADDED
@@ -0,0 +1,57 @@
1
+ # coding: utf-8
2
+
3
+ # Standard
4
+ require 'open3'
5
+
6
+ # Installed
7
+ require 'ansi/selector'
8
+ require 'colorize'
9
+ require 'memoist'
10
+ require 'readline'
11
+ require 'trello'
12
+ require 'yaml'
13
+
14
+ # Internal
15
+ require_relative 'bb_flow/persistent'
16
+ require_relative 'bb_flow/version'
17
+ require_relative 'bb_flow/printer'
18
+ require_relative 'bb_flow/misc'
19
+ require_relative 'bb_flow/pull_requester'
20
+ require_relative 'bb_flow/trello'
21
+ require_relative 'bb_flow/github'
22
+
23
+ module BBFlow
24
+ class Application
25
+ # @param [Hash] options
26
+ #
27
+ # @return [void]
28
+ def self.run(options)
29
+ case options[:command]
30
+ when :commit
31
+ return puts Misc.execute('git commit') if Misc.execute('git diff-index HEAD --name-only --cached').empty?
32
+
33
+ message = options[:message] || Misc.edit
34
+ cards = Trello.cards
35
+ cards = Printer.select_items(cards, 'Which cards does this commit address?') unless cards.blank?
36
+ card_lines = cards.map do |card|
37
+ "Trello Card: #{card.name}, #{card.short_url}"
38
+ end.join("\n")
39
+ # TODO: re-write with plumbing commands
40
+ # e.g. git write-tree | xargs git commit-tree -m'message'
41
+ Misc.execute("git commit -m \"#{message}\n\n#{card_lines}\"")
42
+ commit_sha = Misc.execute('git show-ref --head -s /HEAD/')
43
+ # TODO: convert to http url.
44
+ commit_url = "#{Github.http_url}/commit/#{commit_sha}"
45
+
46
+ cards.each do |card|
47
+ card.add_comment("Commit: #{options[:message]}\n#{commit_url}")
48
+ Printer.success("Added a comment to the „#{card.name}“ card (#{card.url}).")
49
+ end
50
+ when :pr
51
+ PullRequester.create!
52
+ else
53
+ Printer.panic("Incorrect parameter: #{options[:command]}")
54
+ end
55
+ end
56
+ end
57
+ end
metadata ADDED
@@ -0,0 +1,205 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bb-flow
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - gregolsen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ansi-select
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: git
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.9.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.2.9.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: memoist
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.12.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.12.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby-trello
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.3.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: colorize
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.7.7
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.7.7
83
+ - !ruby/object:Gem::Dependency
84
+ name: octokit
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 4.1.1
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 4.1.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.10.6
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.10.6
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.10.2
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.10.2
125
+ - !ruby/object:Gem::Dependency
126
+ name: rake
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 10.4.2
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 10.4.2
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 0.34.2
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 0.34.2
153
+ description:
154
+ email:
155
+ - mihailov.in@gmail.com
156
+ executables:
157
+ - bb-flow
158
+ - git-pr
159
+ extensions: []
160
+ extra_rdoc_files: []
161
+ files:
162
+ - ".gitignore"
163
+ - ".rubocop.yml"
164
+ - ".rubocop_todo.yml"
165
+ - Gemfile
166
+ - Gemfile.lock
167
+ - README.md
168
+ - Rakefile
169
+ - bb-flow.gemspec
170
+ - bin/bb-flow
171
+ - bin/git-pr
172
+ - lib/bb-flow.rb
173
+ - lib/bb_flow.rb
174
+ - lib/bb_flow/github.rb
175
+ - lib/bb_flow/misc.rb
176
+ - lib/bb_flow/persistent.rb
177
+ - lib/bb_flow/printer.rb
178
+ - lib/bb_flow/pull_requester.rb
179
+ - lib/bb_flow/trello.rb
180
+ - lib/bb_flow/version.rb
181
+ homepage: ''
182
+ licenses:
183
+ - MIT
184
+ metadata: {}
185
+ post_install_message:
186
+ rdoc_options: []
187
+ require_paths:
188
+ - lib
189
+ required_ruby_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ required_rubygems_version: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: '0'
199
+ requirements: []
200
+ rubyforge_project:
201
+ rubygems_version: 2.4.8
202
+ signing_key:
203
+ specification_version: 4
204
+ summary: Simplifies typical BrightBytes work flows
205
+ test_files: []