hustle_and_flow 0.0.1 → 0.0.2
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 +4 -4
- data/bin/hustle +5 -0
- data/lib/hustle_and_flow/binary/runner.rb +35 -0
- data/lib/hustle_and_flow/commands/start.rb +36 -0
- data/lib/hustle_and_flow/errors/unknown_issue_action_error.rb +6 -0
- data/lib/hustle_and_flow/formatters/branch_table_formatter.rb +81 -0
- data/lib/hustle_and_flow/formatters/issue_detail_formatter.rb +74 -0
- data/lib/hustle_and_flow/formatters/issue_table_formatter.rb +80 -0
- data/lib/hustle_and_flow/io/shell.rb +102 -0
- data/lib/hustle_and_flow/issue_tracker.rb +18 -0
- data/lib/hustle_and_flow/issue_trackers/github/issue.rb +192 -0
- data/lib/hustle_and_flow/issue_trackers/github/issues.rb +89 -0
- data/lib/hustle_and_flow/issue_trackers/github.rb +71 -0
- data/lib/hustle_and_flow/utils/string.rb +25 -0
- data/lib/hustle_and_flow/utils/url_slug.rb +28 -0
- data/lib/hustle_and_flow/vcs_repository.rb +19 -0
- data/lib/hustle_and_flow/version.rb +1 -1
- data/lib/hustle_and_flow/version_control/git/branch.rb +136 -0
- data/lib/hustle_and_flow/version_control/git/branches.rb +93 -0
- data/lib/hustle_and_flow/version_control/git/repository.rb +99 -0
- data/lib/hustle_and_flow.rb +1 -2
- data/spec/lib/hustle_and_flow/formatters/branch_table_formatter_spec.rb +44 -0
- data/spec/lib/hustle_and_flow/formatters/issue_detail_formatter_spec.rb +49 -0
- data/spec/lib/hustle_and_flow/formatters/issue_table_formatter_spec.rb +89 -0
- data/spec/lib/hustle_and_flow/io/shell_spec.rb +154 -0
- data/spec/lib/hustle_and_flow/issue_tracker_spec.rb +7 -0
- data/spec/lib/hustle_and_flow/issue_trackers/github/issue_spec.rb +354 -0
- data/spec/lib/hustle_and_flow/issue_trackers/github/issues_spec.rb +91 -0
- data/spec/lib/hustle_and_flow/issue_trackers/github_spec.rb +51 -0
- data/spec/lib/hustle_and_flow/vcs_repository_spec.rb +15 -0
- data/spec/lib/hustle_and_flow/version_control/git/branch_spec.rb +101 -0
- data/spec/lib/hustle_and_flow/version_control/git/branches_spec.rb +57 -0
- data/spec/lib/hustle_and_flow/version_control/git/repository_spec.rb +36 -0
- data/spec/lib/utils/url_slug_spec.rb +74 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/git_repo.rb +43 -0
- metadata +128 -16
- data/.gitignore +0 -19
- data/.ruby-version +0 -1
- data/.travis.yml +0 -13
- data/Gemfile +0 -4
- data/Gemfile.lock +0 -56
- data/Rakefile +0 -2
- data/hustle_and_flow.gemspec +0 -37
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'hustle_and_flow/issue_trackers/github/issue'
|
2
|
+
require 'hustle_and_flow/formatters/issue_table_formatter'
|
3
|
+
|
4
|
+
module HustleAndFlow
|
5
|
+
module IssueTrackers
|
6
|
+
class Github
|
7
|
+
class Issues
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
attr_accessor :tracker,
|
11
|
+
:query,
|
12
|
+
:issues
|
13
|
+
|
14
|
+
def initialize(tracker:, issues: nil, query: { state: :all })
|
15
|
+
self.tracker = tracker
|
16
|
+
self.query = query
|
17
|
+
self.issues = issues
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.start(tracker:, issues: nil, io:, me:, issue_data:)
|
21
|
+
new(tracker: tracker,
|
22
|
+
issues: issues).
|
23
|
+
start(io: io,
|
24
|
+
me: me,
|
25
|
+
issue_data: issue_data)
|
26
|
+
end
|
27
|
+
|
28
|
+
def start(io:, me:, issue_data:)
|
29
|
+
issue_data = issue_data.merge(status: 'open')
|
30
|
+
|
31
|
+
unless issue_specified?(issue_data)
|
32
|
+
applicable_issues = filter_by(issue_data)
|
33
|
+
|
34
|
+
io.print_formatted_table \
|
35
|
+
data: Formatters::IssueTableFormatter.new(applicable_issues).to_ary,
|
36
|
+
title: 'Available Issues'
|
37
|
+
|
38
|
+
issue_data[:number] = io.choose_issue until issue_specified?(issue_data)
|
39
|
+
end
|
40
|
+
|
41
|
+
find_or_create(issue_data).start(me: me)
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_or_create(**args)
|
45
|
+
find(**args) || Issue.create(tracker: tracker, **args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def each
|
49
|
+
issues.each do |issue|
|
50
|
+
yield issue
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def filter_by(*args)
|
57
|
+
self.class.new(tracker: tracker,
|
58
|
+
issues: issues.select do |issue|
|
59
|
+
issue if issue.match?(*args)
|
60
|
+
end)
|
61
|
+
end
|
62
|
+
|
63
|
+
def find(type: nil, title: nil, number: nil, **args)
|
64
|
+
issues.find do |issue|
|
65
|
+
issue.match?(type: type, title: title) ||
|
66
|
+
issue.match?(number: number.to_i)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def issues
|
71
|
+
@issues ||= client.
|
72
|
+
issues(tracker.repo_name, query).
|
73
|
+
map do |issue_data|
|
74
|
+
Issue.new(tracker: tracker,
|
75
|
+
data: issue_data)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def issue_specified?(data)
|
80
|
+
(data[:type] && data[:title]) || data[:number]
|
81
|
+
end
|
82
|
+
|
83
|
+
def client
|
84
|
+
tracker.client
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'octokit'
|
2
|
+
require 'hustle_and_flow/issue_trackers/github/issue'
|
3
|
+
require 'hustle_and_flow/issue_trackers/github/issues'
|
4
|
+
|
5
|
+
module HustleAndFlow
|
6
|
+
module IssueTrackers
|
7
|
+
class Github
|
8
|
+
attr_accessor :client,
|
9
|
+
:repo,
|
10
|
+
:source
|
11
|
+
|
12
|
+
def initialize(repo:)
|
13
|
+
Octokit.auto_paginate = true
|
14
|
+
|
15
|
+
self.client = Octokit::Client.new(netrc: true)
|
16
|
+
self.repo = repo
|
17
|
+
end
|
18
|
+
|
19
|
+
def repo_name
|
20
|
+
repo.base_name
|
21
|
+
end
|
22
|
+
|
23
|
+
def start(io:, me:, issue_data:)
|
24
|
+
Issues.start(tracker: self,
|
25
|
+
io: io,
|
26
|
+
me: me,
|
27
|
+
issue_data: issue_data)
|
28
|
+
end
|
29
|
+
|
30
|
+
# def ready_for_review(branch)
|
31
|
+
# #
|
32
|
+
# # No issue number
|
33
|
+
# # Find by title
|
34
|
+
# # Has issue number
|
35
|
+
# # Find by number
|
36
|
+
# #
|
37
|
+
# # Issue found
|
38
|
+
# # Issue closed
|
39
|
+
# # Ask to reopen issue
|
40
|
+
# # Issue open
|
41
|
+
# # Issue being reviewed
|
42
|
+
# # Issue not being reviewed
|
43
|
+
# # Issue not found
|
44
|
+
# # Ask to create issue
|
45
|
+
# #
|
46
|
+
# # Lookup Pull Request from Issue and Version Number
|
47
|
+
# #
|
48
|
+
# # Pull Request Found
|
49
|
+
# # How are PR's found?
|
50
|
+
# #
|
51
|
+
# # PR Title Format: #15 v1 - My Issue Title
|
52
|
+
# #
|
53
|
+
# # Pull Request Closed
|
54
|
+
# # Reopen PR
|
55
|
+
# # Pull Request Open
|
56
|
+
# # Use found PR
|
57
|
+
# # Pull Request Not Found
|
58
|
+
# # Create New PR
|
59
|
+
# #
|
60
|
+
# pull_request = PullRequest.find_or_create_from_branch_name(branch.name)
|
61
|
+
# issue = Issue.find_or_create_from_branch_name(branch.name)
|
62
|
+
#
|
63
|
+
# issue.ready_for_review(reviewer_names: branch.overwritten_authors)
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# def update_pull_request(branch)
|
67
|
+
# client.create_pull_request()
|
68
|
+
# end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module HustleAndFlow
|
2
|
+
module Utils
|
3
|
+
module String
|
4
|
+
def underscore(camel_cased_word)
|
5
|
+
word = camel_cased_word.to_s.gsub('::', '/')
|
6
|
+
word.gsub!(/(?:([A-Za-z\d])|^)(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}" }
|
7
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
8
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
9
|
+
word.tr!('-', '_')
|
10
|
+
word.chomp!('_')
|
11
|
+
word.downcase!
|
12
|
+
word
|
13
|
+
end
|
14
|
+
|
15
|
+
def titleize(other)
|
16
|
+
result = other.to_s.dup
|
17
|
+
result = underscore(result)
|
18
|
+
result = result.tr('_', ' ')
|
19
|
+
result = result.downcase
|
20
|
+
|
21
|
+
result.gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
###
|
2
|
+
# Public: Responsible for converting strings into URL Slugs
|
3
|
+
#
|
4
|
+
module HustleAndFlow
|
5
|
+
module Utils
|
6
|
+
class UrlSlug
|
7
|
+
def initialize(original_string)
|
8
|
+
self.original_string = original_string || ''
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
original_string.
|
13
|
+
downcase. # Lowercase the string
|
14
|
+
strip. # Remove all leading and trailing spaces
|
15
|
+
gsub(/-+\z/, ''). # Remove any dashes at the end
|
16
|
+
gsub(/\A-+/, ''). # Remove any dashes at the beginning
|
17
|
+
gsub(/[^\w\-\s]|_/, ''). # Remove any non-alphanumerics, dashes or spaces
|
18
|
+
gsub(/\s+/, '-'). # Consolidate remaining consecutive space and
|
19
|
+
# convert to dashes
|
20
|
+
gsub(/-+/, '-') # Consolidate consecutive dashes into one dash
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
attr_accessor :original_string
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'hustle_and_flow/version_control/git/repository'
|
2
|
+
|
3
|
+
module HustleAndFlow
|
4
|
+
class VcsRepository
|
5
|
+
attr_accessor :adapter
|
6
|
+
|
7
|
+
def initialize(**args)
|
8
|
+
self.adapter = HustleAndFlow::
|
9
|
+
VersionControl::
|
10
|
+
Git::
|
11
|
+
Repository.
|
12
|
+
new(path: args[:path])
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, *args)
|
16
|
+
adapter.public_send(name, *args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module HustleAndFlow
|
2
|
+
module VersionControl
|
3
|
+
module Git
|
4
|
+
class Branch
|
5
|
+
attr_accessor :repo,
|
6
|
+
:branch
|
7
|
+
|
8
|
+
def initialize(repo:, branch:)
|
9
|
+
self.repo = repo
|
10
|
+
self.branch = branch
|
11
|
+
end
|
12
|
+
|
13
|
+
def current?
|
14
|
+
local? && branch.current
|
15
|
+
end
|
16
|
+
|
17
|
+
def local?
|
18
|
+
!remote?
|
19
|
+
end
|
20
|
+
|
21
|
+
def remote?
|
22
|
+
!branch.remote.to_s.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_tracking_branch?
|
26
|
+
local? && upstream
|
27
|
+
end
|
28
|
+
|
29
|
+
def author
|
30
|
+
latest_commit.author.name
|
31
|
+
end
|
32
|
+
|
33
|
+
def name
|
34
|
+
branch.name
|
35
|
+
end
|
36
|
+
|
37
|
+
def start
|
38
|
+
# WIP if necessary
|
39
|
+
|
40
|
+
repo.with_branch(name) do
|
41
|
+
ensure_upstream
|
42
|
+
end
|
43
|
+
|
44
|
+
# if selected_branch.has_wip_commit? && yes?("There is a WIP commit on this bra
|
45
|
+
# nch, would you like to apply it locally?")
|
46
|
+
# repo.rollback
|
47
|
+
#
|
48
|
+
# say "The WIP commit has been applied."
|
49
|
+
#
|
50
|
+
# if yes?("Would you like to remove the commit from origin?")
|
51
|
+
# repo.push(branch: selected_branch, force: true)
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
end
|
55
|
+
|
56
|
+
def version_number
|
57
|
+
name[/v(\d+)\z/, 1].to_i
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# def has_wip_commit?
|
63
|
+
# latest_commit.message.include? 'WIP:'
|
64
|
+
# end
|
65
|
+
|
66
|
+
# def <=>(other)
|
67
|
+
# name <=> other.name
|
68
|
+
# end
|
69
|
+
|
70
|
+
# def overwritten_authors
|
71
|
+
# names = `git overwritten`
|
72
|
+
# names.first(3)
|
73
|
+
# end
|
74
|
+
|
75
|
+
def ensure_upstream
|
76
|
+
self.upstream = default_upstream_branch_name unless has_tracking_branch?
|
77
|
+
end
|
78
|
+
|
79
|
+
def latest_commit
|
80
|
+
branch.gcommit
|
81
|
+
end
|
82
|
+
|
83
|
+
def upstream=(other)
|
84
|
+
remote, branch = parse_name(other)
|
85
|
+
|
86
|
+
self.upstream_remote = remote
|
87
|
+
self.upstream_branch = "refs/heads/#{branch}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def upstream
|
91
|
+
return nil if upstream_remote.empty?
|
92
|
+
|
93
|
+
"#{upstream_remote}/#{name}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def upstream_remote
|
97
|
+
repo.upstream_remote_for_branch(name)
|
98
|
+
end
|
99
|
+
|
100
|
+
def upstream_remote=(other)
|
101
|
+
repo.upstream_remote_for_branch(name, other)
|
102
|
+
end
|
103
|
+
|
104
|
+
def upstream_branch
|
105
|
+
repo.upstream_branch_for_branch(name)
|
106
|
+
end
|
107
|
+
|
108
|
+
def upstream_branch=(other)
|
109
|
+
repo.upstream_branch_for_branch(name, other)
|
110
|
+
end
|
111
|
+
|
112
|
+
def default_upstream_branch_name
|
113
|
+
"origin/#{name}"
|
114
|
+
end
|
115
|
+
|
116
|
+
def parse_name(name)
|
117
|
+
name = name.to_s
|
118
|
+
|
119
|
+
if name.match(%r{^(?:remotes/)?([^/]+)/(.+)})
|
120
|
+
return [$1, $2]
|
121
|
+
end
|
122
|
+
|
123
|
+
return [nil, name]
|
124
|
+
end
|
125
|
+
|
126
|
+
def branch=(other)
|
127
|
+
@branch = if other.is_a? self.class
|
128
|
+
other.branch
|
129
|
+
else
|
130
|
+
other
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'hustle_and_flow/utils/url_slug'
|
2
|
+
require 'hustle_and_flow/version_control/git/branch'
|
3
|
+
|
4
|
+
module HustleAndFlow
|
5
|
+
module VersionControl
|
6
|
+
module Git
|
7
|
+
class Branches
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
attr_accessor :repo,
|
11
|
+
:branches
|
12
|
+
|
13
|
+
def initialize(repo:, branches:)
|
14
|
+
self.repo = repo
|
15
|
+
self.branches = branches
|
16
|
+
end
|
17
|
+
|
18
|
+
def filter_by_issue(number:, title:)
|
19
|
+
self.class.new(repo: repo,
|
20
|
+
branches: (branches_matching_issue_number(number) +
|
21
|
+
branches_matching_issue_title(title)).uniq)
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid_number?(number)
|
25
|
+
return false unless number.to_s.match(/\A\d+\z/)
|
26
|
+
|
27
|
+
(1..branches.count).include?(number.to_i)
|
28
|
+
end
|
29
|
+
|
30
|
+
def from_number(number)
|
31
|
+
branches[number.to_i - 1]
|
32
|
+
end
|
33
|
+
|
34
|
+
def next_branch_name(template:)
|
35
|
+
template.sub('*****', next_version_number.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
def selectable
|
39
|
+
tracking_branches = branches.map do |branch|
|
40
|
+
branch.name if branch.has_tracking_branch?
|
41
|
+
end
|
42
|
+
|
43
|
+
self.class.new(repo: repo,
|
44
|
+
branches: branches.reject do |branch|
|
45
|
+
branch.remote? &&
|
46
|
+
tracking_branches.include?(branch.name)
|
47
|
+
end)
|
48
|
+
end
|
49
|
+
|
50
|
+
def each
|
51
|
+
branches.each do |branch|
|
52
|
+
yield branch
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def next_version_number
|
59
|
+
latest_branch = branches.max do |first_branch, second_branch|
|
60
|
+
first_branch.version_number <=> second_branch.version_number
|
61
|
+
end
|
62
|
+
|
63
|
+
latest_branch ? latest_branch.version_number + 1 : 1
|
64
|
+
end
|
65
|
+
|
66
|
+
def branches_matching_issue_number(issue_number)
|
67
|
+
return [] unless issue_number
|
68
|
+
|
69
|
+
branches.select do |branch|
|
70
|
+
branch.name.match(/-#{issue_number.to_i}\b/)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def branches_matching_issue_title(issue_title)
|
75
|
+
return [] unless issue_title.to_s.length > 0
|
76
|
+
|
77
|
+
title_slug = Utils::UrlSlug.new(issue_title).to_s
|
78
|
+
|
79
|
+
branches.select do |branch|
|
80
|
+
branch.name.include? title_slug
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def branches=(other)
|
85
|
+
@branches = other.map do |branch|
|
86
|
+
Branch.new(repo: repo,
|
87
|
+
branch: branch)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'git'
|
2
|
+
require 'hustle_and_flow/version_control/git/branches'
|
3
|
+
require 'hustle_and_flow/formatters/branch_table_formatter'
|
4
|
+
|
5
|
+
module HustleAndFlow
|
6
|
+
module VersionControl
|
7
|
+
module Git
|
8
|
+
class Repository
|
9
|
+
attr_accessor :repo
|
10
|
+
|
11
|
+
def initialize(path:)
|
12
|
+
self.repo = ::Git.open(path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def start(io:, number:, title:, branch_template:)
|
16
|
+
available_branches = branches.
|
17
|
+
selectable.
|
18
|
+
filter_by_issue(number: number,
|
19
|
+
title: title)
|
20
|
+
|
21
|
+
next_branch_name = available_branches.next_branch_name(template: branch_template)
|
22
|
+
|
23
|
+
selected = if available_branches.count.zero?
|
24
|
+
Branch.new(repo: self, branch: repo.checkout(next_branch_name,
|
25
|
+
new_branch: true))
|
26
|
+
else
|
27
|
+
branch_number = nil
|
28
|
+
|
29
|
+
io.print_formatted_table \
|
30
|
+
title: 'Possible Existing Branches',
|
31
|
+
data: Formatters::BranchTableFormatter.new(available_branches).
|
32
|
+
to_ary
|
33
|
+
|
34
|
+
io.print_new_branch_prompt(next_branch_name)
|
35
|
+
|
36
|
+
until available_branches.valid_number? branch_number
|
37
|
+
branch_number = io.choose_branch
|
38
|
+
end
|
39
|
+
|
40
|
+
available_branches.from_number(branch_number)
|
41
|
+
end
|
42
|
+
|
43
|
+
selected.start
|
44
|
+
end
|
45
|
+
|
46
|
+
def user
|
47
|
+
repo.config('github.user')
|
48
|
+
end
|
49
|
+
|
50
|
+
def base_name
|
51
|
+
origin_url[%r{github\.com.([\w\-]+/[\w\-]+)}, 1]
|
52
|
+
end
|
53
|
+
|
54
|
+
def upstream_remote_for_branch(branch_name, value = nil)
|
55
|
+
repo.config(upstream_remote_config_key(branch_name), value)
|
56
|
+
end
|
57
|
+
|
58
|
+
def upstream_branch_for_branch(branch_name, value = nil)
|
59
|
+
repo.config(upstream_merge_config_key(branch_name), value)
|
60
|
+
end
|
61
|
+
|
62
|
+
def with_branch(branch_name)
|
63
|
+
repo.checkout(branch_name)
|
64
|
+
|
65
|
+
yield
|
66
|
+
|
67
|
+
repo.push('origin', branch_name)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def branches
|
73
|
+
Branches.new(repo: self,
|
74
|
+
branches: repo.branches)
|
75
|
+
end
|
76
|
+
|
77
|
+
def new_branch_name(branch_template)
|
78
|
+
branch_template.sub('*****', branches.next_version_number)
|
79
|
+
end
|
80
|
+
|
81
|
+
def origin_url
|
82
|
+
origin.url
|
83
|
+
end
|
84
|
+
|
85
|
+
def origin
|
86
|
+
@origin ||= repo.remote('origin')
|
87
|
+
end
|
88
|
+
|
89
|
+
def upstream_remote_config_key(branch_name)
|
90
|
+
"branch.#{branch_name}.remote"
|
91
|
+
end
|
92
|
+
|
93
|
+
def upstream_merge_config_key(branch_name)
|
94
|
+
"branch.#{branch_name}.merge"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/hustle_and_flow.rb
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hustle_and_flow/formatters/branch_table_formatter'
|
3
|
+
|
4
|
+
module HustleAndFlow
|
5
|
+
module Formatters
|
6
|
+
describe BranchTableFormatter do
|
7
|
+
it 'can output branch information as columns' do
|
8
|
+
branches = [
|
9
|
+
OpenStruct.new(current?: true,
|
10
|
+
local?: true,
|
11
|
+
remote?: true,
|
12
|
+
has_tracking_branch?: true,
|
13
|
+
author: 'Michael Myers',
|
14
|
+
name: 'feature/add-new-feature-closes-42-v2'),
|
15
|
+
OpenStruct.new(current?: false,
|
16
|
+
local?: false,
|
17
|
+
remote?: true,
|
18
|
+
has_tracking_branch?: false,
|
19
|
+
author: 'Jeff Has a Really Really Long Name',
|
20
|
+
name: 'add-new-feature-42'),
|
21
|
+
]
|
22
|
+
|
23
|
+
columnizer = BranchTableFormatter.new(branches)
|
24
|
+
|
25
|
+
expect(columnizer.to_ary).to eql([
|
26
|
+
[
|
27
|
+
{ format: [:white, :bold], value: 1 },
|
28
|
+
{ format: [:yellow, :bold], value: '*' },
|
29
|
+
{ format: [:yellow, :bold], value: 'T' },
|
30
|
+
{ format: [:white, :bold], value: 'Michael Myers' },
|
31
|
+
{ format: [:green, :bold], value: 'feature/add-new-feature-closes-42-v2' },
|
32
|
+
],
|
33
|
+
[
|
34
|
+
{ format: [:white, :bold], value: 2 },
|
35
|
+
{ format: [:yellow, :bold], value: ' ' },
|
36
|
+
{ format: [:magenta, :bold], value: 'R' },
|
37
|
+
{ format: [:white, :bold], value: 'Jeff Has a Really Really Long Name' },
|
38
|
+
{ format: [:green, :bold], value: 'add-new-feature-42' },
|
39
|
+
],
|
40
|
+
])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hustle_and_flow/formatters/issue_detail_formatter'
|
3
|
+
require 'hustle_and_flow/issue_trackers/github/issue'
|
4
|
+
|
5
|
+
module HustleAndFlow
|
6
|
+
module Formatters
|
7
|
+
describe IssueDetailFormatter do
|
8
|
+
it 'can output the details of an issue as an array' do
|
9
|
+
issue = IssueTrackers::Github::Issue.new(
|
10
|
+
tracker: 'foo',
|
11
|
+
data: {
|
12
|
+
status: 'open',
|
13
|
+
assignee: {
|
14
|
+
login: 'neo' },
|
15
|
+
number: 42,
|
16
|
+
title: 'My Awesome Title',
|
17
|
+
body: 'My Body is a Wonderland' })
|
18
|
+
|
19
|
+
expect(IssueDetailFormatter.new(issue).to_hash).to eql(
|
20
|
+
header: { format: [:green, :bold], value: '#42 - My Awesome Title' },
|
21
|
+
divider: { format: [:black, :bold], value: nil },
|
22
|
+
contact: { format: [:blue, :bold], value: 'By: neo' },
|
23
|
+
body: { format: [:white, :bold], value: 'My Body is a Wonderland' }
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'can output the category next to the title if there is one' do
|
28
|
+
issue = IssueTrackers::Github::Issue.new(
|
29
|
+
tracker: 'foo',
|
30
|
+
data: {
|
31
|
+
status: 'open',
|
32
|
+
assignee: {
|
33
|
+
login: 'neo' },
|
34
|
+
number: 42,
|
35
|
+
labels: [
|
36
|
+
{ name: 'feature' },
|
37
|
+
],
|
38
|
+
title: 'My Awesome Title' })
|
39
|
+
|
40
|
+
expect(IssueDetailFormatter.new(issue).to_hash).to eql(
|
41
|
+
header: { format: [:green, :bold], value: '#42 - Feature: My Awesome Title' },
|
42
|
+
divider: { format: [:black, :bold], value: nil },
|
43
|
+
contact: { format: [:blue, :bold], value: 'By: neo' },
|
44
|
+
body: { format: [:white, :bold], value: nil }
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|