m-git 2.5.4

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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +85 -0
  4. data/lib/m-git.rb +66 -0
  5. data/lib/m-git/argv.rb +170 -0
  6. data/lib/m-git/argv/opt.rb +38 -0
  7. data/lib/m-git/argv/opt_list.rb +71 -0
  8. data/lib/m-git/argv/parser.rb +66 -0
  9. data/lib/m-git/base_command.rb +271 -0
  10. data/lib/m-git/command/add.rb +41 -0
  11. data/lib/m-git/command/branch.rb +90 -0
  12. data/lib/m-git/command/checkout.rb +106 -0
  13. data/lib/m-git/command/clean.rb +64 -0
  14. data/lib/m-git/command/commit.rb +84 -0
  15. data/lib/m-git/command/config.rb +202 -0
  16. data/lib/m-git/command/delete.rb +99 -0
  17. data/lib/m-git/command/fetch.rb +32 -0
  18. data/lib/m-git/command/forall.rb +81 -0
  19. data/lib/m-git/command/info.rb +74 -0
  20. data/lib/m-git/command/init.rb +324 -0
  21. data/lib/m-git/command/log.rb +73 -0
  22. data/lib/m-git/command/merge.rb +381 -0
  23. data/lib/m-git/command/pull.rb +364 -0
  24. data/lib/m-git/command/push.rb +311 -0
  25. data/lib/m-git/command/rebase.rb +348 -0
  26. data/lib/m-git/command/reset.rb +31 -0
  27. data/lib/m-git/command/self.rb +223 -0
  28. data/lib/m-git/command/stash.rb +189 -0
  29. data/lib/m-git/command/status.rb +135 -0
  30. data/lib/m-git/command/sync.rb +327 -0
  31. data/lib/m-git/command/tag.rb +67 -0
  32. data/lib/m-git/command_manager.rb +24 -0
  33. data/lib/m-git/error.rb +20 -0
  34. data/lib/m-git/foundation.rb +25 -0
  35. data/lib/m-git/foundation/constants.rb +107 -0
  36. data/lib/m-git/foundation/dir.rb +25 -0
  37. data/lib/m-git/foundation/duration_recorder.rb +92 -0
  38. data/lib/m-git/foundation/git_message_parser.rb +50 -0
  39. data/lib/m-git/foundation/lock.rb +32 -0
  40. data/lib/m-git/foundation/loger.rb +129 -0
  41. data/lib/m-git/foundation/mgit_config.rb +222 -0
  42. data/lib/m-git/foundation/operation_progress_manager.rb +139 -0
  43. data/lib/m-git/foundation/timer.rb +74 -0
  44. data/lib/m-git/foundation/utils.rb +361 -0
  45. data/lib/m-git/hooks_manager.rb +96 -0
  46. data/lib/m-git/manifest.rb +181 -0
  47. data/lib/m-git/manifest/cache_manager.rb +44 -0
  48. data/lib/m-git/manifest/internal.rb +182 -0
  49. data/lib/m-git/manifest/light_repo.rb +108 -0
  50. data/lib/m-git/manifest/light_repo_generator.rb +87 -0
  51. data/lib/m-git/manifest/linter.rb +153 -0
  52. data/lib/m-git/open_api.rb +427 -0
  53. data/lib/m-git/open_api/script_download_info.rb +37 -0
  54. data/lib/m-git/output/output.rb +461 -0
  55. data/lib/m-git/plugin_manager.rb +112 -0
  56. data/lib/m-git/repo.rb +133 -0
  57. data/lib/m-git/repo/status.rb +481 -0
  58. data/lib/m-git/repo/sync_helper.rb +254 -0
  59. data/lib/m-git/template.rb +9 -0
  60. data/lib/m-git/template/local_manifest.rb +27 -0
  61. data/lib/m-git/template/manifest_hook.rb +28 -0
  62. data/lib/m-git/template/post_download_hook.rb +29 -0
  63. data/lib/m-git/template/post_hook.rb +31 -0
  64. data/lib/m-git/template/pre_exec_hook.rb +31 -0
  65. data/lib/m-git/template/pre_hook.rb +29 -0
  66. data/lib/m-git/template/pre_push_hook.rb +32 -0
  67. data/lib/m-git/version.rb +6 -0
  68. data/lib/m-git/workspace.rb +648 -0
  69. data/lib/m-git/workspace/path_helper.rb +56 -0
  70. data/lib/m-git/workspace/workspace_helper.rb +159 -0
  71. data/m-git +1 -0
  72. data/mgit +19 -0
  73. metadata +218 -0
@@ -0,0 +1,74 @@
1
+ #coding=utf-8
2
+
3
+ module MGit
4
+ # 计时器,用于统计指令执行耗时
5
+ class Timer
6
+
7
+ @@time_stamp = {}
8
+ @@duration = {}
9
+ @@lock = Mutex.new
10
+
11
+ class << self
12
+
13
+ # 开始计时
14
+ #
15
+ # @param repo_name [String] 仓库名
16
+ #
17
+ # @param use_lock [Boolean] default: false 是否加锁
18
+ #
19
+ def start(repo_name, use_lock:false)
20
+ return if repo_name.nil?
21
+ mutex_exec(use_lock) {
22
+ @@time_stamp[repo_name] = Time.new if @@time_stamp[repo_name].nil?
23
+ @@duration[repo_name] = 0 if @@duration[repo_name].nil?
24
+ }
25
+ end
26
+
27
+ # 停止计时
28
+ #
29
+ # @param repo_name [String] 仓库名
30
+ #
31
+ # @param use_lock [Boolean] default: false 是否加锁
32
+ #
33
+ def stop(repo_name, use_lock:false)
34
+ return if repo_name.nil?
35
+ mutex_exec(use_lock) {
36
+ if !@@time_stamp[repo_name].nil? && !@@duration[repo_name].nil?
37
+ @@duration[repo_name] += Time.new.to_f - @@time_stamp[repo_name].to_f
38
+ @@time_stamp[repo_name] = nil
39
+ end
40
+ }
41
+ end
42
+
43
+ # 显示最耗时仓库
44
+ #
45
+ # @param threshold [Type] default: 5 耗时提示阈值,时间超过该阈值则将仓库纳入提醒集合
46
+ #
47
+ def show_time_consuming_repos(threshold:5)
48
+ repos = []
49
+ @@duration.sort_by { |repo_name,seconds| seconds }.reverse.first(5).each { |info|
50
+ repo_name = info.first
51
+ seconds = info.last
52
+ repos.push("[#{seconds.round(2)}s]#{repo_name}") if seconds > threshold
53
+ }
54
+ Output.puts_remind_block(repos, "以上为最耗时且耗时超过#{threshold}s的仓库,请自行关注影响速度的原因。") if repos.length > 0
55
+ end
56
+
57
+ # 多线程执行保护
58
+ #
59
+ # @param use_lock [Boolean] 执行是否加锁
60
+ #
61
+ def mutex_exec(use_lock)
62
+ if use_lock
63
+ @@lock.lock
64
+ yield if block_given?
65
+ @@lock.unlock
66
+ else
67
+ yield if block_given?
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,361 @@
1
+ #coding=utf-8
2
+
3
+ module MGit
4
+ module Utils
5
+
6
+ class << self
7
+ def logical_cpu_num
8
+ if @logical_cpu_num.nil?
9
+ begin
10
+ num = `sysctl -n hw.logicalcpu`
11
+ num = `cat /proc/cpuinfo | grep "processor" | wc -l` unless $?.success?
12
+ @logical_cpu_num = num
13
+ rescue Exception => _
14
+ @logical_cpu_num = "5"
15
+ end
16
+ end
17
+ @logical_cpu_num.to_i
18
+ end
19
+
20
+ # ----- Shell指令相关 -----
21
+
22
+ # 执行shell指令
23
+ def execute_shell_cmd(cmd)
24
+ begin
25
+ (stdout, stderr, status) = Open3.capture3(cmd)
26
+ rescue => e
27
+ puts "\n"
28
+ Foundation.help!("指令 \"#{cmd}\" 执行异常:#{e.message}")
29
+ end
30
+ yield(stdout, stderr, status) if block_given?
31
+ self
32
+ end
33
+
34
+ # ----- 目录相关 -----
35
+
36
+ # 改变当前路径
37
+ def change_dir(dir)
38
+ return if dir == Dir.pwd
39
+ begin
40
+ Dir.chdir(dir)
41
+ rescue => e
42
+ raise "目录切换失败:#{e.message}"
43
+ end
44
+ end
45
+
46
+ # 在某路径下执行代码
47
+ def execute_under_dir(dir)
48
+ origin_dir = Dir.pwd
49
+ change_dir(dir)
50
+ yield() if block_given?
51
+ change_dir(origin_dir)
52
+ end
53
+
54
+ # 计算相对目录
55
+ #
56
+ # @param dir_a [String] 目录A,如‘/a/b/A’
57
+ #
58
+ # @param dir_b [String] 目录B,如‘/a/c/B’
59
+ #
60
+ # @return [String] A目录下文件相对B目录的路径,如‘../../c/B’
61
+ #
62
+ def relative_dir(dir_a, dir_b, realpath: true)
63
+ if realpath
64
+ (Pathname.new(dir_a).realpath.relative_path_from(Pathname.new(dir_b).realpath)).to_s
65
+ else
66
+ (Pathname.new(dir_a).relative_path_from(Pathname.new(dir_b))).to_s
67
+ end
68
+ end
69
+
70
+ # 扩展成完整路径
71
+ #
72
+ # @param path [String] 路径名
73
+ #
74
+ # @param base [Type] default: nil 基准路径
75
+ #
76
+ # @return [String] 扩展后的完整路径
77
+ #
78
+ def expand_path(path, base:nil)
79
+ pn = Pathname.new(path)
80
+ if pn.relative?
81
+ base = Dir.pwd if base.nil?
82
+ File.expand_path(File.join(base, path))
83
+ else
84
+ path
85
+ end
86
+ end
87
+
88
+ # 初始化缓存目录
89
+ #
90
+ # @return [String] 目录地址
91
+ #
92
+ def generate_init_cache_path(root)
93
+ temp_dir = ".#{Constants::INIT_CACHE_DIR_NAME}__#{Process.pid}__#{Time.new.to_i.to_s}"
94
+ File.join(root,temp_dir)
95
+ end
96
+
97
+ # 创建软连接
98
+ #
99
+ # @param target_path [String] 目标文件(文件夹)的绝对路径
100
+ #
101
+ # @param link_path [String] 软连接所在绝对路径
102
+ #
103
+ def link(target_path, link_path)
104
+ target_relative_path = File.join(relative_dir(File.dirname(target_path), File.dirname(link_path)), File.basename(target_path))
105
+ FileUtils.symlink(target_relative_path, link_path, force:true)
106
+ end
107
+
108
+ # 显示下载仓库信息
109
+ #
110
+ # @param missing_light_repos [Array<LightRepo>] 缺失仓库配置对象
111
+ #
112
+ def show_clone_info(root, missing_light_repos)
113
+ notice_repo = []
114
+ clone_from_local = []
115
+ clone_from_remote = []
116
+ missing_light_repos.each { |light_repo|
117
+ if Dir.exist?(File.join(light_repo.git_store_dir(root), '.git'))
118
+ clone_from_local += [light_repo.name]
119
+ else
120
+ clone_from_remote += [light_repo.name]
121
+ end
122
+ }
123
+
124
+ notice_repo.push(['从本地导出', clone_from_local]) if clone_from_local.length > 0
125
+ notice_repo.push(['从远程下载', clone_from_remote]) if clone_from_remote.length > 0
126
+
127
+ puts Output.generate_table_combination(notice_repo, separator: "|")
128
+ Output.puts_processing_message('以上仓库本地缺失,处理中...')
129
+ end
130
+
131
+ # ----- Git相关 -----
132
+
133
+ # 在不拉取仓库的情况下,查询远程仓库是否存在某分支
134
+ #
135
+ # @return [Bool] 是否存在分支
136
+ #
137
+ def branch_exist_on_remote?(branch, git_url)
138
+ return false if branch.nil? || git_url.nil?
139
+ cmd = "git ls-remote --heads #{git_url} | grep \"#{branch}\""
140
+ execute_shell_cmd(cmd) { |stdout, stderr, status|
141
+ return status.success?
142
+ }
143
+ end
144
+
145
+ # 在不拉仓库的情况下,查询当前用户是否有权限拉取代码
146
+ #
147
+ # @return [Bool] 是否有权限
148
+ #
149
+ def has_permission_of_remote?(git_url)
150
+ return false if git_url.nil?
151
+ cmd = "git ls-remote --heads #{git_url}"
152
+ execute_shell_cmd(cmd) { |stdout, stderr, status|
153
+ return status.success?
154
+ }
155
+ end
156
+
157
+ # 链接.git仓库实体
158
+ # 1、如果git实体存在,则删除,以当前仓库的.git为主
159
+ # 2、移动仓库的.git到git实体
160
+ # 3、软链仓库的.git为git实体
161
+ #
162
+ # @param source_dir [String] 源码路径
163
+ #
164
+ # @param source_git_dir [String] 存放git实体的路径
165
+ #
166
+ #
167
+ #
168
+ def link_git(source_dir, source_git_dir)
169
+
170
+ # 工作区.git
171
+ origin_git = File.join(source_dir, '.git')
172
+
173
+ # 本地缓存的.git
174
+ target_git = File.join(source_git_dir, '.git')
175
+
176
+ FileUtils.remove_dir(target_git, true) if File.exist?(target_git)
177
+ FileUtils.mkdir_p(source_git_dir) unless File.exist?(source_git_dir)
178
+
179
+ FileUtils.mv(origin_git, source_git_dir)
180
+
181
+ # 创建.git软链接
182
+ link(target_git, origin_git)
183
+ end
184
+
185
+ # 根据url生成git实体存放路径
186
+ #
187
+ # @param url [String] 仓库url
188
+ #
189
+ # @return [String] .git实体存放地址,生成错误返回nil
190
+ #
191
+ def generate_git_store(root, url)
192
+ return if url.nil?
193
+ git_dir = File.join(root, Constants::PROJECT_DIR[:source_git])
194
+ begin
195
+ url_obj = URI(url)
196
+ # 去除shceme
197
+ url_path = File.join("#{url_obj.host}#{url_obj.port.nil? ? '' : ":#{url_obj.port}"}", url_obj.path)
198
+ # 去除后缀名
199
+ git_relative_dir = File.join(File.dirname(url_path), File.basename(url_path, File.extname(url_path)))
200
+
201
+ File.join(git_dir, git_relative_dir)
202
+ rescue
203
+ end
204
+ end
205
+
206
+ # ----- 工作区同步相关 -----
207
+
208
+ # 同步工作区(缓存或弹出)
209
+ #
210
+ # @param root [String] mgit管理工程根目录
211
+ #
212
+ # @param config [Manifest] 配置对象
213
+ #
214
+ # @param recover_cache_if_cancelled [Boolean] 如果回收过程中取消操作,是否恢复缓存
215
+ # (需要自行判断,如果方法调用前缓存已经覆盖,那么需要恢复以保障下次同步操作正常执行
216
+ # 如果调用前缓存未被覆盖,则无需恢复,此时若强行恢复会干扰下次同步操作)
217
+ #
218
+ def sync_workspace(root, config, recover_cache_if_cancelled:true)
219
+
220
+ # 若有缓存仓库,则移到工作区
221
+ config.light_repos.each { |light_repo|
222
+ #【注意】url不一致会认为是不同仓库,将缓存当前仓库,并弹出url对应缓存(若对应缓存则不弹出)
223
+ Workspace.sync_workspace(root, light_repo)
224
+ }
225
+
226
+ # 更新冗余仓库数据
227
+ if config.previous_extra_light_repos.nil? || config.previous_extra_light_repos.length == 0
228
+ config.update_previous_extra_light_repos(root)
229
+ end
230
+
231
+ # 若工作区有多余仓库,则缓存
232
+ if !config.previous_extra_light_repos.nil? && config.previous_extra_light_repos.length > 0
233
+
234
+ dirty_repos = []
235
+ do_repos = []
236
+ config.previous_extra_light_repos.each { |light_repo|
237
+
238
+ # 如果仓库是主仓库,则不操作
239
+ next if light_repo.is_config_repo
240
+
241
+ repo, error = Repo.generate_softly(root, light_repo)
242
+ if error.nil?
243
+ if repo.status_checker.status == Repo::Status::GIT_REPO_STATUS[:dirty]
244
+ dirty_repos.push(repo)
245
+ else
246
+ do_repos.push(repo)
247
+ end
248
+ end
249
+ }
250
+
251
+ if dirty_repos.length > 0
252
+ if Output.continue_with_interact_repos?(dirty_repos.map { |e| e.name }, '即将回收以上仓库,但存在本地改动,继续操作将丢失改动,是否取消?') ||
253
+ Output.continue_with_user_remind?("即将丢失改动,是否取消?")
254
+ # 用上次配置覆盖以恢复缓存,否则若此次缓存已被覆盖,取消后下次操作同步将失效
255
+ config.update_cache_with_content(root, config.previous_config) if recover_cache_if_cancelled
256
+ Foundation.help!('操作取消')
257
+ end
258
+ end
259
+
260
+ current_time = Time.new.strftime("%Y%m%d%H%M%S")
261
+ (dirty_repos + do_repos).each { |repo|
262
+ if dirty_repos.include?(repo)
263
+ repo.execute_git_cmd('add', '.')
264
+ repo.execute_git_cmd('stash', "save -u #{current_time}_MGit回收仓库自动stash")
265
+ end
266
+
267
+ begin
268
+ save_to_cache = MGitConfig.query_with_key(root, :savecache)
269
+ rescue Error => _
270
+ save_to_cache = false
271
+ end
272
+
273
+ # 如果仓库没有被管理,则不删除,直接缓存
274
+ is_git_managed = Dir.exist?(File.join(repo.config.git_store_dir(root), '.git'))
275
+ if save_to_cache || !is_git_managed
276
+ Workspace.push(root, repo.config)
277
+ else
278
+ FileUtils.rm_rf(repo.path) if Dir.exist?(repo.path)
279
+ end
280
+ }
281
+
282
+ end
283
+ end
284
+
285
+ def pop_git_entity(root, config)
286
+ config.light_repos.each { |light_repo|
287
+ # 将托管的.git弹出到工作区
288
+ Workspace.pop_git_entity(root, light_repo)
289
+ }
290
+ end
291
+
292
+ def push_git_entity(root, config)
293
+ config.light_repos.each { |light_repo|
294
+ # 将工作区的.git托管给mgit
295
+ Workspace.push_git_entity(root, light_repo)
296
+ }
297
+ end
298
+
299
+ # 判断url是否一致
300
+ #
301
+ # @param url_a [String] url
302
+ #
303
+ # @param url_b [String] url
304
+ #
305
+ # @return [Boolean] 是否一致
306
+ #
307
+ def url_consist?(url_a, url_b)
308
+ # 删除冗余字符
309
+ temp_a = normalize_url(url_a)
310
+ temp_b = normalize_url(url_b)
311
+
312
+ # 同时不为nil判断内容是否相等
313
+ return temp_a == temp_b if !temp_a.nil? && !temp_b.nil?
314
+ # reutrn | temp_a | temp_b
315
+ # ----------------------
316
+ # true | nil | nil
317
+ # true | '' | nil
318
+ # true | nil | ''
319
+ # ----------------------
320
+ # false | nil | xxx
321
+ # false | xxx | nil
322
+ (temp_a.nil? && temp_b.nil?) || (!temp_a.nil? && temp_a.length == 0) || (!temp_b.nil? && temp_b.length == 0)
323
+ end
324
+
325
+ # 规范化url,删除冗余字符
326
+ #
327
+ # @param url [String] url字符串
328
+ #
329
+ # @return [String] 规范化后的url
330
+ #
331
+ def normalize_url(url)
332
+ return if url.nil?
333
+ refined_url = url.strip.sub(/(\/)+$/,'')
334
+ return refined_url if refined_url.length == 0
335
+ begin
336
+ uri = URI(refined_url)
337
+ return "#{uri.scheme}://#{uri.host}#{uri.port.nil? ? '' : ":#{uri.port}"}#{uri.path}"
338
+ rescue => _
339
+ end
340
+ end
341
+
342
+ # 安全的路径拼接
343
+ #
344
+ # @param path_a [String] 路径a
345
+ #
346
+ # @param path_b [String] 路径b
347
+ #
348
+ # @return [String] 完整路径
349
+ #
350
+ def safe_join(path_a, path_b)
351
+ if !path_a.nil? && path_a.length > 0 && !path_b.nil? && path_b.length > 0
352
+ File.join(path_a, path_b)
353
+ elsif !path_a.nil? && path_a.length > 0
354
+ path_a
355
+ elsif !path_b.nil? && path_b.length > 0
356
+ path_b
357
+ end
358
+ end
359
+ end
360
+ end
361
+ end
@@ -0,0 +1,96 @@
1
+
2
+ module MGit
3
+ module HooksManager
4
+
5
+ # --- 执行hook ---
6
+
7
+ class << self
8
+ # 获取配置表前执行的hook
9
+ #
10
+ # @param strict_mode [Boolean] default: true 严格模式下出错直接终止,非严格模式下出错抛出异常
11
+ #
12
+ # @return [Type] description_of_returned_object
13
+ #
14
+ def execute_manifest_hook(strict_mode:true)
15
+ __execute_hook_file(Constants::HOOK_NAME[:manifest_hook], 'MGitTemplate::ManifestHook') do |cls|
16
+ begin
17
+ cls.run
18
+ rescue Error => e
19
+ if strict_mode
20
+ Foundation.help!("配置表生成失败:#{e.msg}") if e.type == MGIT_ERROR_TYPE[:config_generate_error]
21
+ else
22
+ raise e
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ # mgit执行前的hook
29
+ def execute_mgit_pre_hook(cmd, pure_opts)
30
+ __execute_hook_file(Constants::HOOK_NAME[:pre_hook], 'MGitTemplate::PreHook') do |cls|
31
+ cls.run(cmd, pure_opts, Workspace.root)
32
+ end
33
+ end
34
+
35
+ # mgit执行后的hook
36
+ def execute_mgit_post_hook(cmd, pure_opts, light_repos)
37
+ __execute_hook_file(Constants::HOOK_NAME[:post_hook], 'MGitTemplate::PostHook') do |cls|
38
+ cls.run(cmd, pure_opts, Workspace.root, light_repos)
39
+ end
40
+ end
41
+
42
+ # mgit执行前的hook(用户级,此时已经完成状态检查,可以在内部获取到仓库配置对象)
43
+ # 可以按需插入到不同指令的执行前时机下调用,然后在方法中通过'cmd'参数判断当前执行到是哪个指令
44
+ # 目前仅插入到commit指令,后续可按需插入
45
+ def execute_mgit_pre_exec_hook(cmd, pure_opts, light_repos)
46
+ __execute_hook_file(Constants::HOOK_NAME[:pre_exec_hook], 'MGitTemplate::PreExecHook') do |cls|
47
+ cls.run(cmd, pure_opts, Workspace.root, light_repos)
48
+ end
49
+ end
50
+
51
+ # 功能类似'execute_mgit_pre_exec_hook',但仅仅是push指令专用(内部不用判断'cmd',cmd一定是push,可替换为'execute_mgit_pre_exec_hook')
52
+ def execute_mgit_pre_push_hook(cmd, pure_opts, light_repos)
53
+ __execute_hook_file(Constants::HOOK_NAME[:pre_push_hook], 'MGitTemplate::PrePushHook') do |cls|
54
+ cls.run(cmd, pure_opts, Workspace.root, light_repos)
55
+ end
56
+ end
57
+
58
+ # 执行下载后的hook
59
+ #
60
+ # @param repo_name [String] 仓库ming
61
+ #
62
+ # @param repo_path [String] 仓库本地路径
63
+ #
64
+ # @param root [String] .mgit所在目录
65
+ #
66
+ # @param error [String] 错误信息,nil表示成功
67
+ #
68
+ # @return [Boolean] hook是否操作过仓库分支
69
+ #
70
+ def execute_post_download_hook(repo_name, repo_path)
71
+ changed = __execute_hook_file(Constants::HOOK_NAME[:post_download_hook], 'MGitTemplate::PostDownloadHook') do |cls|
72
+ cls.run(repo_name, repo_path)
73
+ end
74
+ changed == true
75
+ end
76
+
77
+ # 执行hook文件
78
+ #
79
+ # @param file_name [String] hook文件名
80
+ #
81
+ # @param hook_class [String] hook Class name
82
+ #
83
+ # block
84
+ def __execute_hook_file(file_name, hook_class)
85
+ file_path = File.join(Workspace.hooks_dir, file_name)
86
+ if File.exists?(file_path)
87
+ require file_path
88
+ end
89
+ if Object.const_defined?(hook_class) && hook = Object.const_get(hook_class)
90
+ return yield(hook) if block_given?
91
+ end
92
+ end
93
+ end
94
+
95
+ end
96
+ end