lhj-tools 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/lib/lhj/command.rb CHANGED
@@ -1,10 +1,13 @@
1
1
  require 'claide'
2
2
  require "tty-spinner"
3
+ require 'lhj/action/sh_helper'
4
+ require 'lhj/ui/ui'
3
5
 
4
6
  module Lhj
5
7
  # command plugin
6
8
  class Command < CLAide::Command
7
9
  require 'lhj/command/init'
10
+ require 'lhj/command/config'
8
11
  require 'lhj/command/head_import'
9
12
  require 'lhj/command/refactor_rename'
10
13
  require 'lhj/command/local/fetch'
@@ -20,21 +23,36 @@ module Lhj
20
23
  require 'lhj/command/rename_image'
21
24
  require 'lhj/command/trans'
22
25
  require 'lhj/command/yapi'
26
+ require 'lhj/command/file_path'
27
+ require 'lhj/command/http'
28
+ require 'lhj/command/sync_pod_repo'
23
29
 
24
30
  self.abstract_command = true
25
31
  self.command = 'lhj'
26
32
 
27
- def spinner
28
- @spinner ||= TTY::Spinner.new('[:spinner]正在处理...', output: $stdout, format: :dots)
29
- end
30
-
31
33
  def auto_spin
32
- spinner.auto_spin
33
- puts "\n"
34
+ print "正在处理...\n".green
35
+ # @spinner.auto_spin
34
36
  end
35
37
 
36
38
  def stop
37
- spinner.stop('Done!')
39
+ print '处理完成'.green
40
+ # @spinner.success('Done!')
41
+ end
42
+
43
+ def initialize(argv)
44
+ super(argv)
45
+ @spinner = TTY::Spinner.new('...', output: $stdout, format: :dots, clear: true)
46
+ end
47
+
48
+ def run
49
+ auto_spin
50
+ handle
51
+ stop
52
+ end
53
+
54
+ def handle
55
+ raise 'A subclass should override the `Lhj::Command#run` method'
38
56
  end
39
57
 
40
58
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lhj
4
4
  module Tools
5
- VERSION = "0.1.6"
5
+ VERSION = "0.1.7"
6
6
  end
7
7
  end
data/lib/lhj/tools.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative "tools/version"
3
+ require 'colored'
3
4
 
4
5
  module Lhj
5
6
  require 'lhj/config'
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'pathname'
4
4
 
5
- require 'lhj/tree/node'
5
+ require_relative 'node'
6
6
 
7
7
  module Lhj
8
8
  class Tree
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'pathname'
4
-
5
- require 'lhj/tree/node'
4
+ require_relative 'node'
6
5
 
7
6
  module Lhj
8
7
  class Tree
data/lib/lhj/tree/tree.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'lhj/tree/node'
4
- require 'lhj/tree/directory_renderer'
5
- require 'lhj/tree/number_renderer'
6
- require 'lhj/tree/hash_walker'
7
- require 'lhj/tree/path_walker'
3
+ require_relative 'node'
4
+ require_relative 'directory_renderer'
5
+ require_relative 'number_renderer'
6
+ require_relative 'hash_walker'
7
+ require_relative 'path_walker'
8
8
 
9
9
  module Lhj
10
10
  class Tree
@@ -0,0 +1,19 @@
1
+ require_relative 'lhj_exception'
2
+
3
+ module Lhj
4
+ class Interface
5
+ # Super class for exception types that we do not want to record
6
+ # explicitly as crashes or user errors
7
+ class LhjCommonException < LhjException; end
8
+
9
+ # Raised when there is a build failure in xcodebuild
10
+ class LhjBuildFailure < LhjCommonException; end
11
+
12
+ # Raised when a test fails when being run by tools such as scan or snapshot
13
+ class LhjTestFailure < LhjCommonException; end
14
+
15
+ # Raise this type of exception when a failure caused by a third party
16
+ # dependency (i.e. xcodebuild, gradle, slather) happens.
17
+ class LhjDependencyCausedException < LhjCommonException; end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'lhj_exception'
2
+
3
+ module Lhj
4
+ class Interface
5
+ class LhjCrash < LhjException
6
+ def prefix
7
+ '[LHJ_CRASH]'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ require_relative 'lhj_exception'
2
+
3
+ module Lhj
4
+ class Interface
5
+ class LhjError < LhjException
6
+ attr_reader :show_github_issues
7
+ attr_reader :error_info
8
+
9
+ def initialize(show_github_issues: false, error_info: nil)
10
+ @show_github_issues = show_github_issues
11
+ @error_info = error_info
12
+ end
13
+
14
+ def prefix
15
+ '[USER_ERROR]'
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ class Exception
22
+ # def fastlane_should_report_metrics?
23
+ # return false
24
+ # end
25
+ end
@@ -0,0 +1,19 @@
1
+ module Lhj
2
+ class Interface
3
+ class LhjException < StandardError
4
+ def prefix
5
+ '[LHJ_EXCEPTION]'
6
+ end
7
+
8
+ def caused_by_calling_ui_method?(method_name: nil)
9
+ return false if backtrace.nil? || backtrace[0].nil? || method_name.nil?
10
+ first_frame = backtrace[0]
11
+ if first_frame.include?(method_name) && first_frame.include?('interface.rb')
12
+ true
13
+ else
14
+ false
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'lhj_exception'
2
+
3
+ module Lhj
4
+ class Interface
5
+ class LhjShellError < LhjException
6
+ def prefix
7
+ '[SHELL_ERROR]'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1 @@
1
+ Dir[File.dirname(__FILE__) + "/errors/*.rb"].each { |f| require_relative f }
@@ -0,0 +1,148 @@
1
+ require_relative '../interface'
2
+
3
+ module Lhj
4
+ # Shell is the terminal output of things
5
+ # For documentation for each of the methods open `interface.rb`
6
+ class Shell < Interface
7
+
8
+ def log
9
+ return @log if @log
10
+
11
+ $stdout.sync = true
12
+
13
+ # if !ENV.key?('DEBUG')
14
+ # $stdout.puts("Logging disabled while running tests. Force them by setting the DEBUG environment variable")
15
+ # @log ||= Logger.new(nil) # don't show any logs when running tests
16
+ # else
17
+ @log ||= Logger.new($stdout)
18
+ # end
19
+
20
+ @log.formatter = proc do |severity, datetime, progname, msg|
21
+ "#{format_string(datetime, severity)}#{msg}\n"
22
+ end
23
+
24
+ @log
25
+ end
26
+
27
+ def format_string(datetime = Time.now, severity = "")
28
+ return "[#{datetime.strftime('%H:%M:%S')}]: "
29
+ end
30
+
31
+ #####################################################
32
+ # @!group Messaging: show text to the user
33
+ #####################################################
34
+
35
+ def error(message)
36
+ log.error(message.to_s.red)
37
+ end
38
+
39
+ def important(message)
40
+ log.warn(message.to_s.yellow)
41
+ end
42
+
43
+ def success(message)
44
+ log.info(message.to_s.green)
45
+ end
46
+
47
+ def message(message)
48
+ log.info(message.to_s)
49
+ end
50
+
51
+ def deprecated(message)
52
+ log.error(message.to_s.deprecated)
53
+ end
54
+
55
+ def command(message)
56
+ log.info("$ #{message}".cyan)
57
+ end
58
+
59
+ def command_output(message)
60
+ actual = (encode_as_utf_8_if_possible(message).split("\r").last || "") # as clearing the line will remove the `>` and the time stamp
61
+ actual.split("\n").each do |msg|
62
+ prefix = msg.include?("▸") ? "" : "▸ "
63
+ log.info(prefix + "" + msg.magenta)
64
+ end
65
+ end
66
+
67
+ def verbose(message)
68
+ log.debug(message.to_s)
69
+ end
70
+
71
+ def header(message)
72
+ success(message)
73
+ end
74
+
75
+ def content_error(content, error_line)
76
+ error_line = error_line.to_i
77
+ return unless error_line > 0
78
+
79
+ contents = content.split(/\r?\n/).map(&:chomp)
80
+
81
+ start_line = error_line - 2 < 1 ? 1 : error_line - 2
82
+ end_line = error_line + 2 < contents.length ? error_line + 2 : contents.length
83
+
84
+ Range.new(start_line, end_line).each do |line|
85
+ str = line == error_line ? " => " : " "
86
+ str << line.to_s.rjust(Math.log10(end_line) + 1)
87
+ str << ":\t#{contents[line - 1]}"
88
+ error(str)
89
+ end
90
+ end
91
+
92
+ #####################################################
93
+ # @!group Errors: Inputs
94
+ #####################################################
95
+
96
+ def interactive?
97
+ interactive = true
98
+ interactive = false if $stdout.isatty == false
99
+ interactive = false if Helper.ci?
100
+ return interactive
101
+ end
102
+
103
+ def input(message)
104
+ verify_interactive!(message)
105
+ ask("#{format_string}#{message.to_s.yellow}").to_s.strip
106
+ end
107
+
108
+ def confirm(message)
109
+ verify_interactive!(message)
110
+ agree("#{format_string}#{message.to_s.yellow} (y/n)", true)
111
+ end
112
+
113
+ def select(message, options)
114
+ verify_interactive!(message)
115
+
116
+ important(message)
117
+ choose(*options)
118
+ end
119
+
120
+ def password(message)
121
+ verify_interactive!(message)
122
+
123
+ ask("#{format_string}#{message.to_s.yellow}") do |q|
124
+ q.whitespace = :chomp
125
+ q.echo = "*"
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ def encode_as_utf_8_if_possible(message)
132
+ return message if message.valid_encoding?
133
+
134
+ # genstrings outputs UTF-16, so we should try to use this encoding if it turns out to be valid
135
+ test_message = message.dup
136
+ return message.encode(Encoding::UTF_8, Encoding::UTF_16) if test_message.force_encoding(Encoding::UTF_16).valid_encoding?
137
+
138
+ # replace any invalid with empty string
139
+ message.encode(Encoding::UTF_8, invalid: :replace)
140
+ end
141
+
142
+ def verify_interactive!(message)
143
+ return if interactive?
144
+ important(message)
145
+ crash!("Could not retrieve response as fastlane runs in non-interactive mode")
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,205 @@
1
+ require_relative 'errors'
2
+
3
+ module Lhj
4
+ # Abstract super class
5
+ class Interface
6
+ #####################################################
7
+ # @!group Messaging: show text to the user
8
+ #####################################################
9
+
10
+ # Level Error: Can be used to show additional error
11
+ # information before actually raising an exception
12
+ # or can be used to just show an error from which
13
+ # fastlane can recover (much magic)
14
+ #
15
+ # By default those messages are shown in red
16
+ def error(_message)
17
+ not_implemented(__method__)
18
+ end
19
+
20
+ # Level Important: Can be used to show warnings to the user
21
+ # not necessarily negative, but something the user should
22
+ # be aware of.
23
+ #
24
+ # By default those messages are shown in yellow
25
+ def important(_message)
26
+ not_implemented(__method__)
27
+ end
28
+
29
+ # Level Success: Show that something was successful
30
+ #
31
+ # By default those messages are shown in green
32
+ def success(_message)
33
+ not_implemented(__method__)
34
+ end
35
+
36
+ # Level Message: Show a neutral message to the user
37
+ #
38
+ # By default those messages shown in white/black
39
+ def message(_message)
40
+ not_implemented(__method__)
41
+ end
42
+
43
+ # Level Deprecated: Show that a particular function is deprecated
44
+ #
45
+ # By default those messages shown in strong blue
46
+ def deprecated(_message)
47
+ not_implemented(__method__)
48
+ end
49
+
50
+ # Level Command: Print out a terminal command that is being
51
+ # executed.
52
+ #
53
+ # By default those messages shown in cyan
54
+ def command(_message)
55
+ not_implemented(__method__)
56
+ end
57
+
58
+ # Level Command Output: Print the output of a command with
59
+ # this method
60
+ #
61
+ # By default those messages shown in magenta
62
+ def command_output(_message)
63
+ not_implemented(__method__)
64
+ end
65
+
66
+ # Level Verbose: Print out additional information for the
67
+ # users that are interested. Will only be printed when
68
+ # FastlaneCore::Globals.verbose? = true
69
+ #
70
+ # By default those messages are shown in white
71
+ def verbose(_message)
72
+ not_implemented(__method__)
73
+ end
74
+
75
+ # Print a header = a text in a box
76
+ # use this if this message is really important
77
+ def header(_message)
78
+ not_implemented(__method__)
79
+ end
80
+
81
+ # Print lines of content around specific line where
82
+ # failed to parse.
83
+ #
84
+ # This message will be shown as error
85
+ def content_error(content, error_line)
86
+ not_implemented(__method__)
87
+ end
88
+
89
+ #####################################################
90
+ # @!group Errors: Inputs
91
+ #####################################################
92
+
93
+ # Is is possible to ask the user questions?
94
+ def interactive?
95
+ not_implemented(__method__)
96
+ end
97
+
98
+ # get a standard text input (single line)
99
+ def input(_message)
100
+ not_implemented(__method__)
101
+ end
102
+
103
+ # A simple yes or no question
104
+ def confirm(_message)
105
+ not_implemented(__method__)
106
+ end
107
+
108
+ # Let the user select one out of x items
109
+ # return value is the value of the option the user chose
110
+ def select(_message, _options)
111
+ not_implemented(__method__)
112
+ end
113
+
114
+ # Password input for the user, text field shouldn't show
115
+ # plain text
116
+ def password(_message)
117
+ not_implemented(__method__)
118
+ end
119
+
120
+ #####################################################
121
+ # @!group Abort helper methods
122
+ #####################################################
123
+
124
+ # Pass an exception to this method to exit the program
125
+ # using the given exception
126
+ # Use this method instead of user_error! if this error is
127
+ # unexpected, e.g. an invalid server response that shouldn't happen
128
+ def crash!(exception)
129
+ raise LhjCrash.new, exception.to_s
130
+ end
131
+
132
+ # Use this method to exit the program because of an user error
133
+ # e.g. app doesn't exist on the given Developer Account
134
+ # or invalid user credentials
135
+ # or scan tests fail
136
+ # This will show the error message, but doesn't show the full
137
+ # stack trace
138
+ # Basically this should be used when you actively catch the error
139
+ # and want to show a nice error message to the user
140
+ def user_error!(error_message, options = {})
141
+ raise LhjError.new(show_github_issues: options[:show_github_issues], error_info: options[:error_info]), error_message.to_s
142
+ end
143
+
144
+ # Use this method to exit the program because of a shell command
145
+ # failure -- the command returned a non-zero response. This does
146
+ # not specify the nature of the error. The error might be from a
147
+ # programming error, a user error, or an expected error because
148
+ # the user of the Fastfile doesn't have their environment set up
149
+ # properly. Because of this, when these errors occur, it means
150
+ # that the caller of the shell command did not adequate error
151
+ # handling and the caller error handling should be improved.
152
+ def shell_error!(error_message, options = {})
153
+ raise LhjShellError.new(options), error_message.to_s
154
+ end
155
+
156
+ # Use this method to exit the program because of a build failure
157
+ # that's caused by the source code of the user. Example for this
158
+ # is that gym will fail when the code doesn't compile or because
159
+ # settings for the project are incorrect.
160
+ # By using this method we'll have more accurate results about
161
+ # fastlane failures
162
+ def build_failure!(error_message, options = {})
163
+ raise LhjBuildFailure.new(options), error_message.to_s
164
+ end
165
+
166
+ # Use this method to exit the program because of a test failure
167
+ # that's caused by the source code of the user. Example for this
168
+ # is that scan will fail when the tests fail.
169
+ # By using this method we'll have more accurate results about
170
+ # fastlane failures
171
+ def test_failure!(error_message)
172
+ raise LhjTestFailure.new, error_message
173
+ end
174
+
175
+ # Use this method to exit the program because of terminal state
176
+ # that is neither the fault of fastlane, nor a problem with the
177
+ # user's input. Using this method instead of user_error! will
178
+ # avoid tracking this outcome as a fastlane failure.
179
+ #
180
+ # e.g. tests ran successfully, but no screenshots were found
181
+ #
182
+ # This will show the message, but hide the full stack trace.
183
+ def abort_with_message!(message)
184
+ raise LhjCommonException.new, message
185
+ end
186
+
187
+ #####################################################
188
+ # @!group Helpers
189
+ #####################################################
190
+ def not_implemented(method_name)
191
+ require_relative 'ui'
192
+ UI.user_error!("Current UI '#{self}' doesn't support method '#{method_name}'")
193
+ end
194
+
195
+ def to_s
196
+ self.class.name.split('::').last
197
+ end
198
+ end
199
+ end
200
+
201
+ class String
202
+ def deprecated
203
+ self.bold.blue
204
+ end
205
+ end
data/lib/lhj/ui/ui.rb ADDED
@@ -0,0 +1,26 @@
1
+ module Lhj
2
+ class UI
3
+ class << self
4
+ attr_accessor(:ui_object)
5
+
6
+ def ui_object
7
+ require_relative 'implementations/shell'
8
+ @ui_object ||= Shell.new
9
+ end
10
+
11
+ def method_missing(method_sym, *args, &_block)
12
+ # not using `responds` because we don't care about methods like .to_s and so on
13
+ require_relative 'interface'
14
+ interface_methods = Lhj::Interface.instance_methods - Object.instance_methods
15
+ UI.user_error!("Unknown method '#{method_sym}', supported #{interface_methods}") unless interface_methods.include?(method_sym)
16
+
17
+ self.ui_object.send(method_sym, *args)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ # Import all available implementations
24
+ Dir[File.dirname(__FILE__) + '/implementations/*.rb'].each do |file|
25
+ require_relative file
26
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhj-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - lihaijian
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-21 00:00:00.000000000 Z
11
+ date: 2022-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: claide
@@ -138,6 +138,40 @@ dependencies:
138
138
  - - "<"
139
139
  - !ruby/object:Gem::Version
140
140
  version: 1.0.0
141
+ - !ruby/object:Gem::Dependency
142
+ name: colored
143
+ requirement: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ type: :runtime
149
+ prerelease: false
150
+ version_requirements: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ - !ruby/object:Gem::Dependency
156
+ name: excon
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 0.71.0
162
+ - - "<"
163
+ - !ruby/object:Gem::Version
164
+ version: 1.0.0
165
+ type: :runtime
166
+ prerelease: false
167
+ version_requirements: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: 0.71.0
172
+ - - "<"
173
+ - !ruby/object:Gem::Version
174
+ version: 1.0.0
141
175
  - !ruby/object:Gem::Dependency
142
176
  name: faraday-cookie_jar
143
177
  requirement: !ruby/object:Gem::Requirement
@@ -238,8 +272,13 @@ extra_rdoc_files: []
238
272
  files:
239
273
  - README.md
240
274
  - bin/lhj
275
+ - lib/lhj/action/sh_helper.rb
241
276
  - lib/lhj/command.rb
277
+ - lib/lhj/command/config.rb
278
+ - lib/lhj/command/config/info.rb
279
+ - lib/lhj/command/file_path.rb
242
280
  - lib/lhj/command/head_import.rb
281
+ - lib/lhj/command/http.rb
243
282
  - lib/lhj/command/init.rb
244
283
  - lib/lhj/command/local/fetch.rb
245
284
  - lib/lhj/command/local/filter.rb
@@ -253,6 +292,7 @@ files:
253
292
  - lib/lhj/command/refactor_rename.rb
254
293
  - lib/lhj/command/rename_image.rb
255
294
  - lib/lhj/command/reverse_import.rb
295
+ - lib/lhj/command/sync_pod_repo.rb
256
296
  - lib/lhj/command/template.rb
257
297
  - lib/lhj/command/trans.rb
258
298
  - lib/lhj/command/view.rb
@@ -270,6 +310,15 @@ files:
270
310
  - lib/lhj/tree/number_renderer.rb
271
311
  - lib/lhj/tree/path_walker.rb
272
312
  - lib/lhj/tree/tree.rb
313
+ - lib/lhj/ui/errors.rb
314
+ - lib/lhj/ui/errors/lhj_common_error.rb
315
+ - lib/lhj/ui/errors/lhj_crash.rb
316
+ - lib/lhj/ui/errors/lhj_error.rb
317
+ - lib/lhj/ui/errors/lhj_exception.rb
318
+ - lib/lhj/ui/errors/lhj_shell_error.rb
319
+ - lib/lhj/ui/implementations/shell.rb
320
+ - lib/lhj/ui/interface.rb
321
+ - lib/lhj/ui/ui.rb
273
322
  homepage:
274
323
  licenses:
275
324
  - MIT