ballantine 0.1.4.pre.beta1 → 0.1.4.pre.beta3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0bf16550e63e8b7c9436b7ac9abe5fdc0f8d10251e36494ad2586c958eab572
4
- data.tar.gz: 72b9ff136bbe2478c58d237d862e3fa26196daf382391ee554e9f5e19de1f698
3
+ metadata.gz: faa1e978d804f0d6db7c1c8b2d0d46a1fee27cd0a1f11c811b34f700fa99ac4d
4
+ data.tar.gz: 17a176cabd94e64d662bde533f605b6d36ebc19f301f6f77df3afc7870fdfedb
5
5
  SHA512:
6
- metadata.gz: 8f22b05c86c438d910a251bb758ab5a4708b5e79db483c66a4eadd2f4603c899ac68d234e4dbae75ef314c66289caea55485e4a91a2bb480d4af1ac0ecd09948
7
- data.tar.gz: f0605cfe1fb7d8c713e0c92d969504545bdb8cb73f725961d0d69c11c7bed36aea45d24b0e5d865df6a339f5b3d15657bc6bf497dce4145f16c98881aa0aad26
6
+ metadata.gz: 014f30ad7a7764ef9d2d8619a62c541de3e23115e9d9eddebf16da3d1e6f958fc5d2567f57d3072907f8df1d398ca9ff8e8f785675095ad95e8adabfa76e0080
7
+ data.tar.gz: eed48b827efdbab0ff935444936431ac08a8ef97b15a9291679ec45f64ea9b56a295ad4ae40049749d516d385e7daaad18017449638af38d7c0579cf2be155ff
@@ -2,16 +2,22 @@
2
2
 
3
3
  module Ballantine
4
4
  class Author
5
- attr_accessor :name, :commits
5
+ include Printable
6
+
7
+ attr_reader :name, :commits_hash
6
8
 
7
9
  class << self
8
10
  # @param [String] name
9
- # @return [Author] author
10
- def find_or_create_by(name)
11
+ # @return [Author, NilClass]
12
+ def find(name:)
11
13
  @_collections = {} unless defined?(@_collections)
12
- return @_collections[name] unless @_collections[name].nil?
14
+ @_collections[name]
15
+ end
13
16
 
14
- @_collections[name] = new(name)
17
+ # @param [String] name
18
+ # @return [Author]
19
+ def find_or_create_by(name:)
20
+ find(name:) || @_collections[name] = new(name:)
15
21
  end
16
22
 
17
23
  # @return [Array<Author>] authors
@@ -23,29 +29,33 @@ module Ballantine
23
29
  end
24
30
 
25
31
  # @param [String] name
26
- def initialize(name)
32
+ def initialize(name:)
27
33
  @name = name
28
- @commits = {}
34
+ @commits_hash = {}
29
35
  end
30
36
 
31
- # @return [NilClass] nil
37
+ # @return [Boolean]
32
38
  def print_commits
33
39
  puts "\n" + "@#{name}".green
34
- @commits.each do |repo, lists|
35
- count, word = retrieve_count_and_word(lists)
36
- puts " > #{repo.blue}: #{count} new #{word}\n"
37
- puts lists
40
+ commits_hash.each do |repo_name, commits|
41
+ count, word = retrieve_count_and_word(commits)
42
+ puts " > #{repo_name.blue}: #{count} new #{word}"
43
+ commits.each do |commit|
44
+ puts_r " - #{commit.hash.yellow} #{commit.subject}", commit.url.gray
45
+ end
38
46
  end
39
- nil
47
+
48
+ true
40
49
  end
41
50
 
42
51
  # returns an array to use slack attachments field
43
52
  # reference: https://api.slack.com/messaging/composing/layouts#building-attachments
44
- # @return [Hash] result
45
- def serialize_commits
46
- message = @commits.map do |repo, lists|
47
- count, word = retrieve_count_and_word(lists)
48
- "*#{repo}*: #{count} new #{word}\n#{lists.join("\n")}"
53
+ # @return [Hash]
54
+ def slack_message
55
+ message = commits_hash.map do |repo_name, commits|
56
+ count, word = retrieve_count_and_word(commits)
57
+ "*#{repo_name}*: #{count} new #{word}\n" \
58
+ "#{commits.map(&:slack_message).join("\n")}"
49
59
  end.join("\n")
50
60
 
51
61
  {
@@ -56,10 +66,10 @@ module Ballantine
56
66
 
57
67
  private
58
68
 
59
- # @param [Array<String>] lists
69
+ # @param [Array<Commit>] commits
60
70
  # @param [Array(Integer, String)] count, word
61
- def retrieve_count_and_word(lists)
62
- count = lists.size
71
+ def retrieve_count_and_word(commits)
72
+ count = commits.size
63
73
  word = count == 1 ? "commit" : "commits"
64
74
  [count, word]
65
75
  end
@@ -2,25 +2,9 @@
2
2
 
3
3
  module Ballantine
4
4
  class CLI < Thor
5
- # reference: https://github.com/desktop/desktop/blob/a7bca44088b105a04714dc4628f4af50f6f179c3/app/src/lib/remote-parsing.ts#L27-L44
6
- GITHUB_REGEXES = [
7
- '^https?://(.+)/(.+)/(.+)\.git/?$', # protocol: https -> https://github.com/oohyun15/ballantine.git | https://github.com/oohyun15/ballantine.git/
8
- '^https?://(.+)/(.+)/(.+)/?$', # protocol: https -> https://github.com/oohyun15/ballantine | https://github.com/oohyun15/ballantine/
9
- '^git@(.+):(.+)/(.+)\.git$', # protocol: ssh -> git@github.com:oohyun15/ballantine.git
10
- '^git@(.+):(.+)/(.+)/?$', # protocol: ssh -> git@github.com:oohyun15/ballantine | git@github.com:oohyun15/ballantine/
11
- '^git:(.+)/(.+)/(.+)\.git$', # protocol: ssh -> git:github.com/oohyun15/ballantine.git
12
- '^git:(.+)/(.+)/(.+)/?$', # protocol: ssh -> git:github.com/oohyun15/ballantine | git:github.com/oohyun15/ballantine/
13
- '^ssh://git@(.+)/(.+)/(.+)\.git$', # protocol: ssh -> ssh://git@github.com/oohyun15/ballantine.git
14
- ].freeze
5
+ include Printable
15
6
 
16
- FILE_GITMODULES = ".gitmodules"
17
-
18
- TYPE_TERMINAL = "terminal"
19
- TYPE_SLACK = "slack"
20
-
21
- DEFAULT_LJUST = 80
22
-
23
- attr_reader :app_name, :main_path, :sub_path, :send_type
7
+ attr_reader :repo
24
8
 
25
9
  class << self
26
10
  def exit_on_failure?; exit(1) end
@@ -47,14 +31,15 @@ module Ballantine
47
31
  raise NotAllowed, "Environment value must be unique."
48
32
  end
49
33
 
50
- @env = Config::AVAILABLE_ENVIRONMENTS.find { |key| options[key] }
51
- raise AssertionFailed, "Environment value must exist: #{@env}" if @env.nil?
34
+ env = Config::AVAILABLE_ENVIRONMENTS.find { |key| options[key] }
35
+ raise AssertionFailed, "Environment value must exist: #{env}" if env.nil?
52
36
 
37
+ conf.env = env
53
38
  value ? conf.set_data(key, value) : conf.print_data(key)
54
39
  end
55
40
 
56
41
  desc "diff [TARGET] [SOURCE]", "Diff commits between TARGET and SOURCE"
57
- option TYPE_SLACK, type: :boolean, aliases: "-s", default: false, desc: "Send to slack using slack webhook URL."
42
+ option Config::TYPE_SLACK, type: :boolean, aliases: "-s", default: false, desc: "Send to slack using slack webhook URL."
58
43
  def diff(target, source = %x(git rev-parse --abbrev-ref HEAD).chomp)
59
44
  # validate arguments
60
45
  validate(target, source, **options)
@@ -63,30 +48,13 @@ module Ballantine
63
48
  system("git pull -f &> /dev/null")
64
49
 
65
50
  # init instance variables
66
- init_variables(**options)
67
-
68
- # find github url, branch
69
- url = github_url(%x(git config --get remote.origin.url).chomp)
70
- current_revision = %x(git rev-parse --abbrev-ref HEAD).chomp
71
-
72
- # get commit hash
73
- from, sub_from = commit_hash(target)
74
- to, sub_to = commit_hash(source)
75
- system("git checkout #{current_revision} -f &> /dev/null")
51
+ init_variables(target, source, **options)
76
52
 
77
53
  # check commits
78
- check_commits(from, to, url)
79
- sub_path.each_with_index do |path, idx|
80
- next if sub_from[idx] == sub_to[idx]
81
-
82
- Dir.chdir(path)
83
- sub_url = github_url(%x(git config --get remote.origin.url).chomp)
84
- check_commits(sub_from[idx], sub_to[idx], sub_url)
85
- Dir.chdir(main_path)
86
- end
54
+ check_commits(**options)
87
55
 
88
- # send commits
89
- send_commits(target, source, from, to, url)
56
+ # print commits
57
+ print_commits(target, source, **options)
90
58
 
91
59
  exit(0)
92
60
  end
@@ -100,7 +68,7 @@ module Ballantine
100
68
 
101
69
  private
102
70
 
103
- def conf; @conf ||= Config.new(@env) end
71
+ def conf; Config.instance end
104
72
 
105
73
  # @param [String] target
106
74
  # @param [String] source
@@ -119,130 +87,58 @@ module Ballantine
119
87
  raise NotAllowed, "ERROR: target(#{target}) and source(#{source}) can't be equal."
120
88
  end
121
89
 
122
- if options[TYPE_SLACK] && !conf.get_data(Config::KEY_SLACK_WEBHOOK)
90
+ if options[Config::TYPE_SLACK] && !conf.get_data(Config::KEY_SLACK_WEBHOOK)
123
91
  raise NotAllowed, "ERROR: Can't find any slack webhook. Set slack webhook using `ballantine config --#{Config::ENV_LOCAL} slack_webhook [YOUR_WEBHOOK]'."
124
92
  end
125
93
 
126
94
  nil
127
95
  end
128
96
 
97
+ # @param [String] target
98
+ # @param [String] source
129
99
  # @param [Hash] options
130
100
  # @return [Boolean]
131
- def init_variables(**options)
132
- @send_type = options[TYPE_SLACK] ? TYPE_SLACK : TYPE_TERMINAL
133
- @app_name = File.basename(%x(git config --get remote.origin.url).chomp, ".git")
134
- @main_path = Dir.pwd
135
- @sub_path =
136
- if Dir[FILE_GITMODULES].any?
137
- file = File.open(FILE_GITMODULES)
138
- lines = file.readlines.map(&:chomp)
139
- file.close
140
- lines.grep(/path =/).map { |line| line[/(?<=path \=).*/, 0].strip }.sort
141
- else
142
- []
143
- end
101
+ def init_variables(target, source, **options)
102
+ conf.print_type = options[Config::TYPE_SLACK] ? Config::TYPE_SLACK : Config::TYPE_TERMINAL
103
+ @repo = Repository.find_or_create_by(
104
+ path: Dir.pwd,
105
+ remote_url: %x(git config --get remote.origin.url).chomp,
106
+ )
107
+
108
+ # init repo
109
+ repo.init_variables(target, source)
144
110
  true
145
111
  end
146
112
 
147
- # @param [String] name
148
- # @return [String] hash
149
- def check_tag(name)
150
- list = %x(git tag -l).split("\n")
151
- return name unless list.grep(name).any?
152
-
153
- system("git fetch origin tag #{name} -f &> /dev/null")
154
- %x(git rev-list -n 1 #{name}).chomp[0...7]
155
- end
156
-
157
- # @param [String] from
158
- # @param [String] to
159
- # @param [String] url
160
- # @return [NilClass] nil
161
- def check_commits(from, to, url)
162
- repo = File.basename(%x(git config --get remote.origin.url).chomp, ".git")
163
- names = %x(git --no-pager log --pretty=format:"%an" #{from}..#{to}).split("\n").uniq.sort
164
- authors = names.map { |name| Author.find_or_create_by(name) }
165
- authors.each do |author|
166
- format = commit_format(url, ljust: DEFAULT_LJUST - 10)
167
- commits =
168
- %x(git --no-pager log --reverse --no-merges --author="#{author.name}" --format="#{format}" --abbrev=7 #{from}..#{to})
169
- .gsub('"', '\"')
170
- .gsub(/[\u0080-\u00ff]/, "")
171
- next if commits.empty?
172
-
173
- author.commits[repo] = commits.split("\n")
174
- end
175
- nil
176
- end
177
-
178
- # @param [String] url
179
- # @return [String] github_url
180
- def github_url(url)
181
- owner, repository = GITHUB_REGEXES.each do |regex|
182
- if (str = url.match(regex))
183
- break [str[2], str[3]]
184
- end
185
- end
186
-
187
- "https://github.com/#{owner}/#{repository}"
188
- end
189
-
190
- # @param [String] hash
191
- # @return [Array(String, Array<String>)] main, sub's hash
192
- def commit_hash(hash)
193
- # check argument is tag
194
- hash = check_tag(hash)
195
-
196
- system("git checkout #{hash} -f &> /dev/null")
197
- system("git pull &> /dev/null")
198
- main_hash = %x(git --no-pager log -1 --format='%h').chomp
199
- sub_hash =
200
- if sub_path.any?
201
- %x(git ls-tree HEAD #{@sub_path.join(" ")}).split("\n").map { |line| line.split(" ")[2] }
202
- else
203
- []
204
- end
205
-
206
- [main_hash, sub_hash]
207
- end
113
+ # @param [Hash] options
114
+ # @return [Boolean]
115
+ def check_commits(**options)
116
+ repo.check_commits
208
117
 
209
- # @param [String] url
210
- # @param [String] format
211
- # @param [Integer] ljust
212
- def commit_format(url, ljust: DEFAULT_LJUST)
213
- case send_type
214
- when TYPE_TERMINAL
215
- " - " + "%h".yellow + " %<(#{ljust})%s " + "#{url}/commit/%H".gray
216
- when TYPE_SLACK
217
- "\\\`<#{url}/commit/%H|%h>\\\` %s - %an"
218
- else raise AssertionFailed, "Unknown send type: #{send_type}"
219
- end
118
+ true
220
119
  end
221
120
 
222
121
  # @param [String] target
223
122
  # @param [String] source
224
- # @param [String] from
225
- # @param [String] to
226
- # @param [String] url
227
- # @return [NilClass] nil
228
- def send_commits(target, source, from, to, url)
123
+ # @param [Hash] options
124
+ # @return [Boolean]
125
+ def print_commits(target, source, **options)
229
126
  authors = Author.all
230
127
  if authors.empty?
231
128
  raise ArgumentError, "ERROR: There is no commits between \"#{target}\" and \"#{source}\""
232
129
  end
233
130
 
234
131
  number = authors.size
235
- last_commit = %x(git --no-pager log --reverse --format="#{commit_format(url, ljust: DEFAULT_LJUST - 22)}" --abbrev=7 #{from}..#{to} -1).strip
236
132
 
237
- case send_type
238
- when TYPE_TERMINAL
239
- puts "Check commits before #{app_name.red} deployment. (#{target.cyan} <- #{source.cyan})".ljust(DEFAULT_LJUST + 34) + " #{url}/compare/#{from}...#{to}".gray
133
+ case conf.print_type
134
+ when Config::TYPE_TERMINAL
135
+ puts_r "Check commits before #{repo.name.red} deployment. (#{target.cyan} <- #{source.cyan})", "#{repo.url}/compare/#{repo.from.hash}...#{repo.to.hash}".gray
240
136
  puts "Author".yellow + ": #{number}"
241
- puts "Last commit".blue + ": #{last_commit}"
137
+ puts_r "#{"Last commit".blue}: #{repo.to.hash.yellow} #{repo.to.subject}", repo.to.url.gray
242
138
  authors.map(&:print_commits)
243
- when TYPE_SLACK
139
+ when Config::TYPE_SLACK
244
140
  # set message for each author
245
- messages = authors.map(&:serialize_commits)
141
+ messages = authors.map(&:slack_message)
246
142
  actor = %x(git config user.name).chomp
247
143
 
248
144
  # send message to slack
@@ -252,17 +148,19 @@ module Ballantine
252
148
  request = Net::HTTP::Post.new(uri)
253
149
  request.content_type = "application/json"
254
150
  request.body = JSON.dump({
255
- "text" => ":white_check_mark: *#{app_name}* deployment request by <@#{actor}> (\`<#{url}/tree/#{from}|#{target}>\` <- \`<#{url}/tree/#{to}|#{source}>\` <#{url}/compare/#{from}...#{to}|compare>)\n:technologist: Author: #{number}\nLast commit: #{last_commit}",
151
+ "text" => ":white_check_mark: *#{repo.name}* deployment request by <@#{actor}>" \
152
+ " (\`<#{repo.url}/tree/#{repo.from.hash}|#{target}>\` <- \`<#{repo.url}/tree/#{repo.to.hash}|#{source}>\` <#{repo.url}/compare/#{repo.from.hash}...#{repo.to.hash}|compare>)" \
153
+ "\n:technologist: Author: #{number}\nLast commit: #{repo.to.slack_message}",
256
154
  "attachments" => messages,
257
155
  })
258
156
  req_options = { use_ssl: uri.scheme == "https" }
259
- response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
260
- http.request(request)
261
- end
157
+ response = Net::HTTP.start(uri.hostname, uri.port, req_options) { |http| http.request(request) }
262
158
  puts response.message
263
159
  else
264
- raise AssertionFailed, "Unknown send type: #{send_type}"
160
+ raise AssertionFailed, "Unknown print type: #{conf.print_type}"
265
161
  end
162
+
163
+ true
266
164
  end
267
165
  end
268
166
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ballantine
4
+ class Commit
5
+ attr_reader :hash, :long_hash, :subject # attributes
6
+ attr_reader :repo, :author # associations
7
+
8
+ class << self
9
+ # @param [String] hash
10
+ # @param [Repository] repo
11
+ # @return [Commit, NilClass]
12
+ def find(hash:, repo:)
13
+ @_collections = {} unless defined?(@_collections)
14
+ @_collections["#{hash[...7]}-#{repo.name}"]
15
+ end
16
+
17
+ # @param [String] hash
18
+ # @param [Repository] repo
19
+ # @return [Commit]
20
+ def find_or_create_by(hash:, repo:)
21
+ find(hash:, repo:) || @_collections["#{hash[...7]}-#{repo.name}"] = new(hash:, repo:)
22
+ end
23
+ end
24
+
25
+ # @param [String] hash
26
+ # @param [Repository] repo
27
+ def initialize(hash:, repo:)
28
+ @hash = hash[...7]
29
+ @repo = repo
30
+ end
31
+
32
+ # @return [Commit]
33
+ def update(**kwargs)
34
+ # TODO: validate keys and values
35
+ kwargs.each { |key, value| instance_variable_set("@#{key}", value) }
36
+ self
37
+ end
38
+
39
+ def url; @url ||= "#{repo.url}/commit/#{long_hash}" end
40
+
41
+ # @return [String]
42
+ def slack_message
43
+ "\`<#{url}|#{hash}>\` #{subject} - #{author.name}"
44
+ end
45
+ end
46
+ end
@@ -2,24 +2,35 @@
2
2
 
3
3
  module Ballantine
4
4
  class Config
5
+ FILE_BALLANTINE_CONFIG = ".ballantine.json"
5
6
  ENV_LOCAL = "local"
6
7
  ENV_GLOBAL = "global"
8
+ TYPE_TERMINAL = "terminal"
9
+ TYPE_SLACK = "slack"
7
10
  AVAILABLE_ENVIRONMENTS = [
8
11
  ENV_LOCAL,
9
12
  ENV_GLOBAL,
10
13
  ].freeze
11
-
12
14
  KEY_SLACK_WEBHOOK = "slack_webhook"
13
15
  AVAILABLE_KEYS = [
14
16
  KEY_SLACK_WEBHOOK,
15
17
  ].freeze
16
18
 
17
- FILE_BALLANTINE_CONFIG = ".ballantine.json"
19
+ attr_reader :data, :loaded
20
+ attr_accessor :env, :print_type
18
21
 
19
- attr_reader :env, :data, :loaded
22
+ class << self
23
+ # @note singleton method
24
+ # @return [Config]
25
+ def instance(...)
26
+ return @_instance if defined?(@_instance)
27
+
28
+ @_instance = new(...)
29
+ end
30
+ end
20
31
 
21
- def initialize(env)
22
- @env = env || ENV_LOCAL
32
+ def initialize
33
+ @env = ENV_LOCAL
23
34
  @data = {}
24
35
  @loaded = false
25
36
  end
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ballantine
4
+ class Repository
5
+ # reference: https://github.com/desktop/desktop/blob/a7bca44088b105a04714dc4628f4af50f6f179c3/app/src/lib/remote-parsing.ts#L27-L44
6
+ GITHUB_REGEXES = [
7
+ '^https?://(.+)/(.+)/(.+)\.git/?$', # protocol: https -> https://github.com/oohyun15/ballantine.git | https://github.com/oohyun15/ballantine.git/
8
+ '^https?://(.+)/(.+)/(.+)/?$', # protocol: https -> https://github.com/oohyun15/ballantine | https://github.com/oohyun15/ballantine/
9
+ '^git@(.+):(.+)/(.+)\.git$', # protocol: ssh -> git@github.com:oohyun15/ballantine.git
10
+ '^git@(.+):(.+)/(.+)/?$', # protocol: ssh -> git@github.com:oohyun15/ballantine | git@github.com:oohyun15/ballantine/
11
+ '^git:(.+)/(.+)/(.+)\.git$', # protocol: ssh -> git:github.com/oohyun15/ballantine.git
12
+ '^git:(.+)/(.+)/(.+)/?$', # protocol: ssh -> git:github.com/oohyun15/ballantine | git:github.com/oohyun15/ballantine/
13
+ '^ssh://git@(.+)/(.+)/(.+)\.git$', # protocol: ssh -> ssh://git@github.com/oohyun15/ballantine.git
14
+ ].freeze
15
+ FILE_GITMODULES = ".gitmodules"
16
+ PARSER_TOKEN = "!#!#"
17
+
18
+ attr_reader :name, :path, :owner, :from, :to # attributes
19
+ attr_reader :main_repo, :sub_repos, :commits # associations
20
+
21
+ class << self
22
+ # @param [String] path
23
+ # @return [Repository, NilClass]
24
+ def find(path:)
25
+ @_collections = {} unless defined?(@_collections)
26
+ @_collections[path]
27
+ end
28
+
29
+ # @param [String] path
30
+ # @param [String] remote_url
31
+ # @return [Repository]
32
+ def find_or_create_by(path:, remote_url: nil)
33
+ find(path:) || @_collections[path] = new(path:, remote_url:)
34
+ end
35
+
36
+ # @return [Array<Repository>]
37
+ def all
38
+ return [] unless defined?(@_collections)
39
+
40
+ @_collections.values
41
+ end
42
+ end
43
+
44
+ # @param [String] path
45
+ # @param [String] remote_url
46
+ def initialize(path:, remote_url:)
47
+ @path = path
48
+ @commits = []
49
+ @sub_repos = retrieve_sub_repos
50
+ @owner, @name = GITHUB_REGEXES.each do |regex|
51
+ str = remote_url.match(regex)
52
+ break [str[2], str[3]] if str
53
+ end
54
+ end
55
+
56
+ def url; @url ||= "https://github.com/#{owner}/#{name}" end
57
+
58
+ # @param [String] target
59
+ # @param [String] source
60
+ # @return [Boolean]
61
+ def init_variables(target, source)
62
+ current_revision = %x(git rev-parse --abbrev-ref HEAD).chomp
63
+
64
+ foo = lambda do |hash, context|
65
+ hash = check_tag(hash)
66
+ system("git checkout #{hash} -f &> /dev/null")
67
+ system("git pull &> /dev/null")
68
+
69
+ hash = %x(git --no-pager log -1 --format='%h').chomp
70
+ commit = Commit.find_or_create_by(
71
+ hash: hash,
72
+ repo: self,
73
+ )
74
+ instance_variable_set("@#{context}", commit)
75
+
76
+ if sub_repos.any?
77
+ %x(git ls-tree HEAD #{sub_repos.map(&:path).join(" ")}).split("\n").map do |line|
78
+ _, _, sub_hash, sub_path = line.split(" ")
79
+ sub_repo = Repository.find(
80
+ path: path + "/" + sub_path,
81
+ )
82
+ sub_commit = Commit.find_or_create_by(
83
+ hash: sub_hash,
84
+ repo: sub_repo,
85
+ )
86
+ sub_repo.instance_variable_set("@#{context}", sub_commit)
87
+ end
88
+ end
89
+ end
90
+
91
+ foo.call(target, "from")
92
+ foo.call(source, "to")
93
+
94
+ system("git checkout #{current_revision} -f &> /dev/null")
95
+
96
+ true
97
+ end
98
+
99
+ # @return [Boolean]
100
+ def check_commits
101
+ authors = retrieve_authors
102
+ authors.each do |author|
103
+ commits = retrieve_commits(author)
104
+ next if commits.empty?
105
+
106
+ author.commits_hash[name] = commits
107
+ # TODO: append `commits` to `repo.commits`
108
+ end
109
+
110
+ if sub_repos.any?
111
+ sub_repos.each do |sub_repo|
112
+ next if sub_repo.from.hash == sub_repo.to.hash
113
+
114
+ Dir.chdir(sub_repo.path)
115
+ sub_repo.check_commits
116
+ end
117
+ Dir.chdir(path)
118
+ end
119
+
120
+ true
121
+ end
122
+
123
+ private
124
+
125
+ def conf; Config.instance end
126
+
127
+ # @param [String] name
128
+ # @return [String] hash
129
+ def check_tag(name)
130
+ list = %x(git tag -l).split("\n")
131
+ return name unless list.grep(name).any?
132
+
133
+ system("git fetch origin tag #{name} -f &> /dev/null")
134
+ %x(git rev-list -n 1 #{name}).chomp[0...7]
135
+ end
136
+
137
+ # @return [String]
138
+ def check_format
139
+ case conf.print_type
140
+ when Config::TYPE_TERMINAL
141
+ " - " + "%h".yellow + " %<(#{ljust})%s " + "#{url}/commit/%H".gray
142
+ when Config::TYPE_SLACK
143
+ "\\\`<#{url}/commit/%H|%h>\\\` %s - %an"
144
+ else
145
+ raise AssertionFailed, "Unknown print type: #{conf.print_type}"
146
+ end
147
+ end
148
+
149
+ # @return [Array<Repository>]
150
+ def retrieve_sub_repos
151
+ gitmodule = path + "/" + FILE_GITMODULES
152
+ return [] unless Dir[gitmodule].any?
153
+
154
+ file = File.open(gitmodule)
155
+ resp = file.read
156
+ file.close
157
+
158
+ resp.split(/\[submodule.*\]/)
159
+ .select { |line| line.match?(/path = /) }
160
+ .sort
161
+ .map do |line|
162
+ line = line.strip
163
+ repo = Repository.find_or_create_by(
164
+ path: path + "/" + line.match(/path = (.*)/)[1],
165
+ remote_url: line.match(/url = (.*)/)[1],
166
+ )
167
+ repo.instance_variable_set("@main_repo", self)
168
+ repo
169
+ end
170
+ end
171
+
172
+ # @return [Array<Author>]
173
+ def retrieve_authors
174
+ %x(git --no-pager log --pretty=format:"%an" #{from.hash}..#{to.hash})
175
+ .split("\n").uniq.sort
176
+ .map { |name| Author.find_or_create_by(name:) }
177
+ end
178
+
179
+ # @param [Author] author
180
+ # @return [Array<Commit>]
181
+ def retrieve_commits(author)
182
+ command = <<~CMD.tr("\n", " ").strip
183
+ git --no-pager log --reverse --no-merges --author="#{author.name}"
184
+ --format="%h#{PARSER_TOKEN}%H#{PARSER_TOKEN}%s"
185
+ --abbrev=7 #{from.hash}..#{to.hash}
186
+ CMD
187
+ results =
188
+ %x(#{command})
189
+ .gsub('"', '\"')
190
+ .gsub(/[\u0080-\u00ff]/, "")
191
+ .split("\n")
192
+
193
+ results.map do |result|
194
+ hash, long_hash, subject = result.split(PARSER_TOKEN)
195
+ Commit.find_or_create_by(
196
+ hash: hash,
197
+ repo: self,
198
+ ).update(
199
+ author: author,
200
+ long_hash: long_hash,
201
+ subject: subject,
202
+ )
203
+ end
204
+ end
205
+ end
206
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ballantine
4
- VERSION = "0.1.4-beta1"
4
+ VERSION = "0.1.4-beta3"
5
5
  end
data/lib/ballantine.rb CHANGED
@@ -3,9 +3,12 @@
3
3
  require "thor"
4
4
  require "json"
5
5
  require_relative "string"
6
+ require_relative "printable"
6
7
  require_relative "ballantine/version"
7
8
  require_relative "ballantine/config"
8
9
  require_relative "ballantine/author"
10
+ require_relative "ballantine/repository"
11
+ require_relative "ballantine/commit"
9
12
  require_relative "ballantine/cli"
10
13
 
11
14
  module Ballantine
data/lib/printable.rb ADDED
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Printable
4
+ # @param [String] msg
5
+ # @param [String] msg_r
6
+ # @return [NilClass]
7
+ def puts_r(msg, msg_r)
8
+ size = rjust_size(msg, msg_r)
9
+ puts msg + msg_r.rjust(size)
10
+ end
11
+
12
+ # @param [String] msg
13
+ # @param [String] msg_r
14
+ # @return [Integer]
15
+ def rjust_size(msg, msg_r)
16
+ sanitized = ->(str) { str.sanitize_colored.size + str.scan(/\p{Hangul}/).size }
17
+ cols - sanitized.call(msg) - sanitized.call(msg_r) + msg_r.size
18
+ end
19
+
20
+ # @return [Integer]
21
+ def cols
22
+ return @_cols if defined?(@_cols)
23
+
24
+ require "io/console"
25
+ _lines, @_cols = IO.console.winsize
26
+ @_cols
27
+ end
28
+ end
data/lib/string.rb CHANGED
@@ -16,4 +16,6 @@ class String
16
16
  def yellow; "#{YELLOW}#{self}#{NC}" end
17
17
  def blue; "#{BLUE}#{self}#{NC}" end
18
18
  def cyan; "#{CYAN}#{self}#{NC}" end
19
+
20
+ def sanitize_colored; gsub(/\e\[\d+;?\d*m/, "") end
19
21
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ballantine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4.pre.beta1
4
+ version: 0.1.4.pre.beta3
5
5
  platform: ruby
6
6
  authors:
7
7
  - oohyun15
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-19 00:00:00.000000000 Z
11
+ date: 2023-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -51,8 +51,11 @@ files:
51
51
  - lib/ballantine.rb
52
52
  - lib/ballantine/author.rb
53
53
  - lib/ballantine/cli.rb
54
+ - lib/ballantine/commit.rb
54
55
  - lib/ballantine/config.rb
56
+ - lib/ballantine/repository.rb
55
57
  - lib/ballantine/version.rb
58
+ - lib/printable.rb
56
59
  - lib/string.rb
57
60
  homepage: https://github.com/oohyun15/ballantine
58
61
  licenses: