bb-flow 0.1.0

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