abak-flow 1.0.10 → 1.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 +15 -0
- data/README.md +49 -34
- data/abak-flow.gemspec +1 -3
- data/lib/abak-flow.rb +18 -14
- data/lib/abak-flow/branch.rb +9 -10
- data/lib/abak-flow/commands/checkup.rb +11 -14
- data/lib/abak-flow/commands/compare.rb +14 -24
- data/lib/abak-flow/commands/configure.rb +108 -0
- data/lib/abak-flow/commands/done.rb +80 -0
- data/lib/abak-flow/commands/publish.rb +62 -0
- data/lib/abak-flow/configuration.rb +66 -43
- data/lib/abak-flow/errors_presenter.rb +31 -0
- data/lib/abak-flow/inspector.rb +36 -0
- data/lib/abak-flow/locale.rb +45 -0
- data/lib/abak-flow/locales/en.yml +78 -40
- data/lib/abak-flow/locales/ru.yml +78 -40
- data/lib/abak-flow/manager.rb +28 -8
- data/lib/abak-flow/pull_request.rb +21 -30
- data/lib/abak-flow/repository.rb +17 -32
- data/lib/abak-flow/request.rb +18 -80
- data/lib/abak-flow/version.rb +1 -1
- data/spec/lib/abak-flow/commands/checkup_spec.rb +7 -7
- data/spec/lib/abak-flow/commands/compare_spec.rb +9 -8
- metadata +41 -76
- data/lib/abak-flow/visitor.rb +0 -63
@@ -0,0 +1,80 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Abak::Flow
|
3
|
+
module Commands
|
4
|
+
class Done
|
5
|
+
include ANSI::Code
|
6
|
+
|
7
|
+
def run(args, options)
|
8
|
+
Checkup.new.process(Array.new, ::Commander::Command::Options.new)
|
9
|
+
|
10
|
+
branch = Branch.new(Manager.git.current_branch)
|
11
|
+
|
12
|
+
if branch.develop? || branch.master?
|
13
|
+
say red {
|
14
|
+
Manager.locale.error(self,
|
15
|
+
'branch.delete_now_allowed', branch: ANSI.bold { branch })
|
16
|
+
}
|
17
|
+
|
18
|
+
exit 105
|
19
|
+
end
|
20
|
+
|
21
|
+
delete_on_remote(branch)
|
22
|
+
|
23
|
+
# TODO : Быть может стоит вынести это в настройки
|
24
|
+
# и позволить выбирать, куда отправлять
|
25
|
+
# при удалении ветки, а по умолчанию использовать master
|
26
|
+
Manager.git.checkout(
|
27
|
+
branch.extract_base_name(if_undef: Branch::MASTER))
|
28
|
+
|
29
|
+
delete_on_local(branch)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def delete_on_remote(branch)
|
35
|
+
print white {
|
36
|
+
Manager.locale.word(self,
|
37
|
+
"deleting", branch: bold { branch },
|
38
|
+
upstream: bold { "#{Manager.repository.origin}" })
|
39
|
+
}
|
40
|
+
|
41
|
+
begin
|
42
|
+
branch.delete_on_remote
|
43
|
+
rescue
|
44
|
+
say_branch_missed(branch, "#{Manager.repository.origin}")
|
45
|
+
else
|
46
|
+
say_done
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def delete_on_local(branch)
|
51
|
+
print white {
|
52
|
+
Manager.locale.word(self,
|
53
|
+
"deleting", branch: bold { branch },
|
54
|
+
upstream: bold { "working tree" })
|
55
|
+
}
|
56
|
+
|
57
|
+
begin
|
58
|
+
branch.delete_on_local
|
59
|
+
rescue
|
60
|
+
say_branch_missed(branch, "working tree")
|
61
|
+
else
|
62
|
+
say_done
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def say_done
|
67
|
+
say green { " " << Manager.locale.word(self, "done") }
|
68
|
+
end
|
69
|
+
|
70
|
+
def say_branch_missed(branch, upstream)
|
71
|
+
puts
|
72
|
+
say yellow {
|
73
|
+
Manager.locale.error(self,
|
74
|
+
"branch.missed_on", branch: branch,
|
75
|
+
upstream: upstream)
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Abak::Flow
|
3
|
+
module Commands
|
4
|
+
class Publish
|
5
|
+
include ANSI::Code
|
6
|
+
|
7
|
+
def run(args, options)
|
8
|
+
Checkup.new.process(Array.new, ::Commander::Command::Options.new)
|
9
|
+
|
10
|
+
head = Branch.new(Manager.git.current_branch)
|
11
|
+
base = Branch.new(options.base || head.extract_base_name)
|
12
|
+
|
13
|
+
title = options.title || head.extract_title
|
14
|
+
body = options.body || head.extract_body
|
15
|
+
|
16
|
+
pr = PullRequest.new(base: base, head: head, title: title, body: body)
|
17
|
+
validate_request(pr)
|
18
|
+
|
19
|
+
say white {
|
20
|
+
Manager.locale.word(self, "updating",
|
21
|
+
branch: bold { head },
|
22
|
+
upstream: bold { "#{Manager.repository.origin}" })
|
23
|
+
}
|
24
|
+
|
25
|
+
head.update
|
26
|
+
|
27
|
+
say white {
|
28
|
+
Manager.locale.word(self, "publicating",
|
29
|
+
branch: bold { "#{Manager.repository.origin.owner}:#{head}" },
|
30
|
+
upstream: bold { "#{Manager.repository.origin}" })
|
31
|
+
}
|
32
|
+
|
33
|
+
publicate_request(pr)
|
34
|
+
|
35
|
+
say green { pr.link }
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def validate_request(request)
|
41
|
+
inspector = Inspector.new(call_method: :valid?, collect_attribute: :errors)
|
42
|
+
inspector.examine(request).on_fail do |insp|
|
43
|
+
say red { Manager.locale.error(self) }
|
44
|
+
say yellow { insp.output }
|
45
|
+
|
46
|
+
exit 106
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def publicate_request(request)
|
51
|
+
inspector = Inspector.new(call_method: :publish, collect_attribute: :errors)
|
52
|
+
inspector.examine(request).on_fail do |insp|
|
53
|
+
say red { Manager.locale.error(self, 'publication.failed') }
|
54
|
+
say yellow { insp.output }
|
55
|
+
|
56
|
+
exit 107
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,68 +1,91 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
require "i18n"
|
3
|
-
require "ruler"
|
4
2
|
|
5
3
|
module Abak::Flow
|
6
4
|
class Configuration
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
# TODO: Add old oauth_login and oauth_token
|
6
|
+
AVAILABLE_OPTIONS = %w{login password locale http_proxy}.map(&:freeze)
|
7
|
+
GITCONFIG_PATTERN = /abak\-flow\.([\w\d\-\_]+)/.freeze
|
8
|
+
|
9
|
+
def initialize(options = nil)
|
10
|
+
@_errors = Hash.new
|
11
|
+
@options = {
|
12
|
+
login: nil,
|
13
|
+
password: nil,
|
14
|
+
locale: "en",
|
15
|
+
http_proxy: ENV["http_proxy"] || ENV["HTTP_PROXY"]
|
16
|
+
}
|
17
|
+
|
18
|
+
options.nil? ? define_options_from_gitconfig
|
19
|
+
: define_options_from_hash(options)
|
20
|
+
|
21
|
+
create_public_instance_methods
|
22
|
+
end
|
11
23
|
|
12
|
-
|
24
|
+
def rewrite(options)
|
25
|
+
define_options_from_hash(options)
|
26
|
+
write_to_gitconfig!
|
27
|
+
end
|
13
28
|
|
14
|
-
def
|
15
|
-
@
|
16
|
-
@
|
29
|
+
def valid?
|
30
|
+
@_errors = Hash.new
|
31
|
+
@_errors["login"] = ['blank'] if @options[:login].to_s.empty?
|
32
|
+
@_errors["password"] = ['blank'] if @options[:password].to_s.empty?
|
17
33
|
|
18
|
-
|
34
|
+
@_errors.empty?
|
19
35
|
end
|
20
36
|
|
21
|
-
def
|
22
|
-
@
|
37
|
+
def errors
|
38
|
+
ErrorsPresenter.new(self, @_errors)
|
39
|
+
end
|
23
40
|
|
24
|
-
|
25
|
-
fact(:oauth_user_not_setup) { oauth_user.nil? }
|
26
|
-
fact(:oauth_token_not_setup) { oauth_token.nil? }
|
41
|
+
private
|
27
42
|
|
28
|
-
|
29
|
-
|
43
|
+
def create_public_instance_methods
|
44
|
+
AVAILABLE_OPTIONS.each do |name|
|
45
|
+
self.class.send(:define_method, name, -> { @options[name.to_sym] })
|
30
46
|
end
|
31
|
-
|
32
|
-
@errors.empty? ? true : false
|
33
47
|
end
|
34
48
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
49
|
+
def write_to_gitconfig!
|
50
|
+
AVAILABLE_OPTIONS.each do |name|
|
51
|
+
value = @options[name.to_sym]
|
38
52
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
setup_locale
|
53
|
+
next if value.nil? || value.empty?
|
54
|
+
Manager.git.lib.global_config_set("abak-flow.#{name}", value)
|
55
|
+
end
|
43
56
|
end
|
44
57
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
58
|
+
def define_options_from_hash(hash)
|
59
|
+
hash.each do|name, value|
|
60
|
+
name = underscore(name.to_s)
|
61
|
+
next unless AVAILABLE_OPTIONS.include?(name)
|
62
|
+
|
63
|
+
@options[name.to_sym] = value
|
64
|
+
end
|
49
65
|
end
|
50
66
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
67
|
+
def define_options_from_gitconfig
|
68
|
+
read_git_config do |name, value|
|
69
|
+
name = underscore(name)
|
70
|
+
next unless AVAILABLE_OPTIONS.include?(name)
|
54
71
|
|
55
|
-
|
56
|
-
|
57
|
-
|
72
|
+
@options[name.to_sym] = value
|
73
|
+
end
|
74
|
+
end
|
58
75
|
|
59
|
-
|
60
|
-
|
76
|
+
def read_git_config(&block)
|
77
|
+
Manager.git.config.each do |option, value|
|
78
|
+
matches = option.match(GITCONFIG_PATTERN)
|
79
|
+
block.call(underscore(matches[1]), value) if matches && matches[1]
|
61
80
|
end
|
62
81
|
end
|
63
82
|
|
64
|
-
def
|
65
|
-
name.
|
83
|
+
def underscore(name)
|
84
|
+
name.tr("-", "_")
|
85
|
+
end
|
86
|
+
|
87
|
+
def dasherize(name)
|
88
|
+
name.tr("_", "-")
|
66
89
|
end
|
67
|
-
end
|
68
|
-
end
|
90
|
+
end # class Configuration
|
91
|
+
end # module Abak::Flow
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require "forwardable"
|
3
|
+
|
4
|
+
module Abak::Flow
|
5
|
+
class ErrorsPresenter
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@errors, :empty?, :each, :each_with_index
|
9
|
+
|
10
|
+
def initialize(object, errors)
|
11
|
+
@object = object
|
12
|
+
@object_errors = errors
|
13
|
+
@errors = create_human_readable_errors
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def create_human_readable_errors
|
19
|
+
@object_errors.map do |field, errors|
|
20
|
+
field_name = Manager.locale.field(@object, field)
|
21
|
+
|
22
|
+
errors = errors.map do |error|
|
23
|
+
error = {field: error, options: Hash.new} unless error.is_a?(Hash)
|
24
|
+
Manager.locale.error(@object, "#{field}.#{error[:field]}", error[:options])
|
25
|
+
end
|
26
|
+
|
27
|
+
"#{field_name} – #{errors * ", "}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require "ansi/code"
|
3
|
+
|
4
|
+
module Abak::Flow
|
5
|
+
class Inspector
|
6
|
+
def initialize(options = Hash.new)
|
7
|
+
@objects = Array.new
|
8
|
+
@call_method = options.fetch(:call_method)
|
9
|
+
@collect_attribute = options.fetch(:collect_attribute)
|
10
|
+
end
|
11
|
+
|
12
|
+
def examine(*args)
|
13
|
+
@objects = args
|
14
|
+
@fail = @objects.map { |x| x.send(@call_method) }.any? { |x| not !!x }
|
15
|
+
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def output
|
20
|
+
@objects.map do |object|
|
21
|
+
next if object.send(@collect_attribute).empty?
|
22
|
+
|
23
|
+
info = ""
|
24
|
+
object.send(@collect_attribute).each_with_index do |inf, idx|
|
25
|
+
info << "\n #{idx + 1}. #{inf}"
|
26
|
+
end
|
27
|
+
|
28
|
+
"#{Manager.locale.name(object)}#{info}"
|
29
|
+
end * "\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_fail(&block)
|
33
|
+
block.call(self) if @fail
|
34
|
+
end
|
35
|
+
end # class Inspector
|
36
|
+
end # module Abak::Flow
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require "i18n"
|
3
|
+
|
4
|
+
module Abak::Flow
|
5
|
+
class Locale
|
6
|
+
FILES = File.join(File.dirname(__FILE__), "locales/*.{rb,yml}").freeze
|
7
|
+
|
8
|
+
def initialize(locale)
|
9
|
+
I18n.enforce_available_locales = false
|
10
|
+
I18n.load_path += Dir.glob(FILES)
|
11
|
+
I18n.locale = locale
|
12
|
+
end
|
13
|
+
|
14
|
+
def name(object)
|
15
|
+
I18n.t("#{namenize object}.name")
|
16
|
+
end
|
17
|
+
|
18
|
+
def field(object, key)
|
19
|
+
I18n.t(key, scope: "#{namenize object}.fields")
|
20
|
+
end
|
21
|
+
|
22
|
+
def word(object, key, options = {})
|
23
|
+
I18n.t(key, options.merge(scope: "#{namenize object}.words"))
|
24
|
+
end
|
25
|
+
|
26
|
+
def error(object, key = nil, options = {})
|
27
|
+
key.nil? ? I18n.t("#{namenize object}.fail", options)
|
28
|
+
: I18n.t(key, options.merge(scope: "#{namenize object}.errors"))
|
29
|
+
end
|
30
|
+
|
31
|
+
def success(object, *args)
|
32
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
33
|
+
key = args[0]
|
34
|
+
|
35
|
+
key.nil? ? I18n.t("#{namenize object}.success", options)
|
36
|
+
: I18n.t(key, options.merge(scope: "#{namenize object}.success"))
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def namenize(object)
|
42
|
+
object.class.name.downcase.gsub(/\:\:/, ".")
|
43
|
+
end
|
44
|
+
end # class Locale
|
45
|
+
end # module Abak::Flow
|
@@ -1,44 +1,82 @@
|
|
1
1
|
en:
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
abak:
|
3
|
+
flow:
|
4
|
+
commands:
|
5
|
+
checkup:
|
6
|
+
fail: You are not prepared!
|
7
|
+
success: Congratulations, you are ready to ROCK :)
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
upstream_not_setup: Repository with name 'upstream' not found
|
14
|
-
recommendations: "Check .git/cofig file"
|
9
|
+
compare:
|
10
|
+
words:
|
11
|
+
updating: "Updating %{branch} → %{upstream}"
|
12
|
+
diverging: "Branches may diverging\nAdvice: switch to branch '%{branch}' and retry operation"
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
14
|
+
configure:
|
15
|
+
fail: Something goes wrong!
|
16
|
+
success: Configuration generated and saved ↓
|
17
|
+
words:
|
18
|
+
email: Email
|
19
|
+
password: Password
|
20
|
+
locale: Locale
|
21
|
+
http_proxy: HTTP proxy
|
22
|
+
sms_otp: SMS OTP
|
23
|
+
configuring: Configuring
|
24
|
+
errors:
|
25
|
+
execution_failed: Github api request failed
|
26
|
+
empty_response: Github service respond with empty response body
|
23
27
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
28
|
+
done:
|
29
|
+
words:
|
30
|
+
deleting: Deleting %{branch} in %{upstream}
|
31
|
+
done: √
|
32
|
+
errors:
|
33
|
+
branch:
|
34
|
+
delete_now_allowed: You can't delete %{branch}
|
35
|
+
missed_on: Branch %{branch} doesn`t exists on %{upstream}
|
36
|
+
|
37
|
+
publish:
|
38
|
+
fail: Goddamned, not again!
|
39
|
+
words:
|
40
|
+
updating: Updating %{branch} → %{upstream}
|
41
|
+
publicating: Creating pull request %{branch} → %{upstream}
|
42
|
+
errors:
|
43
|
+
publication:
|
44
|
+
failed: Some errors occurs while request publicating
|
45
|
+
|
46
|
+
configuration:
|
47
|
+
name: Configuration
|
48
|
+
fields:
|
49
|
+
login: Login
|
50
|
+
password: Password
|
51
|
+
errors:
|
52
|
+
login:
|
53
|
+
blank: Can't be blank
|
54
|
+
password:
|
55
|
+
blank: Can't be blank
|
56
|
+
|
57
|
+
repository:
|
58
|
+
name: Repository
|
59
|
+
fields:
|
60
|
+
origin: Origin
|
61
|
+
upstream: Upstream
|
62
|
+
errors:
|
63
|
+
origin:
|
64
|
+
not_set: Git remote 'origin' not set
|
65
|
+
upstream:
|
66
|
+
not_set: Git remote 'upstream' not set
|
67
|
+
|
68
|
+
pullrequest:
|
69
|
+
name: Pull Request
|
70
|
+
fields:
|
71
|
+
title: Title message
|
72
|
+
body: Body message
|
73
|
+
head: Head branch
|
74
|
+
base: Base branch
|
75
|
+
exception: Exception
|
76
|
+
errors:
|
77
|
+
head:
|
78
|
+
invalid: Branch name is invalid
|
79
|
+
base:
|
80
|
+
invalid: Branch name is invalid
|
81
|
+
exception:
|
82
|
+
message: "%{backtrace}"
|