rufio 0.50.0 → 0.61.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 +4 -4
- data/CHANGELOG.md +52 -0
- data/README.md +180 -594
- data/examples/config.yml +8 -0
- data/lib/rufio/bookmark.rb +6 -30
- data/lib/rufio/bookmark_manager.rb +15 -1
- data/lib/rufio/bookmark_storage.rb +148 -0
- data/lib/rufio/command_completion.rb +20 -5
- data/lib/rufio/command_mode.rb +102 -9
- data/lib/rufio/command_mode_ui.rb +8 -8
- data/lib/rufio/config.rb +54 -54
- data/lib/rufio/config_loader.rb +53 -0
- data/lib/rufio/job_manager.rb +128 -0
- data/lib/rufio/job_mode.rb +146 -0
- data/lib/rufio/keybind_handler.rb +243 -232
- data/lib/rufio/notification_manager.rb +77 -0
- data/lib/rufio/script_config_loader.rb +101 -0
- data/lib/rufio/script_path_manager.rb +386 -0
- data/lib/rufio/script_runner.rb +216 -0
- data/lib/rufio/task_status.rb +118 -0
- data/lib/rufio/terminal_ui.rb +183 -497
- data/lib/rufio/version.rb +1 -1
- data/lib/rufio.rb +9 -4
- data/scripts/test_jobs/build_simulation.sh +29 -0
- data/scripts/test_jobs/deploy_simulation.sh +37 -0
- data/scripts/test_jobs/quick.sh +11 -0
- data/scripts/test_jobs/random_result.sh +23 -0
- data/scripts/test_jobs/slow_fail.sh +10 -0
- data/scripts/test_jobs/slow_success.sh +10 -0
- data/scripts/test_jobs/very_slow.sh +19 -0
- metadata +21 -10
- data/docs/file-preview-optimization-analysis.md +0 -759
- data/docs/file-preview-performance-issue-FIXED.md +0 -547
- data/lib/rufio/project_command.rb +0 -147
- data/lib/rufio/project_log.rb +0 -68
- data/lib/rufio/project_mode.rb +0 -58
data/lib/rufio/config_loader.rb
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'yaml'
|
|
3
4
|
require_relative 'config'
|
|
5
|
+
require_relative 'bookmark_storage'
|
|
4
6
|
|
|
5
7
|
module Rufio
|
|
6
8
|
class ConfigLoader
|
|
7
9
|
CONFIG_PATH = File.expand_path('~/.config/rufio/config.rb').freeze
|
|
10
|
+
YAML_CONFIG_PATH = File.expand_path('~/.config/rufio/config.yml').freeze
|
|
11
|
+
JSON_BOOKMARKS_PATH = File.expand_path('~/.config/rufio/bookmarks.json').freeze
|
|
8
12
|
|
|
9
13
|
class << self
|
|
10
14
|
def load_config
|
|
@@ -60,6 +64,55 @@ module Rufio
|
|
|
60
64
|
load_config[:command_history_size] || 1000
|
|
61
65
|
end
|
|
62
66
|
|
|
67
|
+
# スクリプトパスの配列を取得
|
|
68
|
+
# @return [Array<String>] 展開済みのスクリプトパス
|
|
69
|
+
def script_paths
|
|
70
|
+
yaml_config = load_yaml_config
|
|
71
|
+
paths = yaml_config[:script_paths] || default_script_paths
|
|
72
|
+
expand_script_paths(paths)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# デフォルトのスクリプトパス
|
|
76
|
+
# @return [Array<String>] デフォルトパス
|
|
77
|
+
def default_script_paths
|
|
78
|
+
[File.expand_path('~/.config/rufio/scripts')]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# スクリプトパスを展開
|
|
82
|
+
# @param paths [Array<String>] パスの配列
|
|
83
|
+
# @return [Array<String>] 展開済みのパス
|
|
84
|
+
def expand_script_paths(paths)
|
|
85
|
+
paths.map { |p| File.expand_path(p) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# YAML設定ファイルを読み込む
|
|
89
|
+
# @param path [String, nil] 設定ファイルのパス(nilの場合はデフォルト)
|
|
90
|
+
# @return [Hash] 設定内容
|
|
91
|
+
def load_yaml_config(path = nil)
|
|
92
|
+
config_path = path || YAML_CONFIG_PATH
|
|
93
|
+
return {} unless File.exist?(config_path)
|
|
94
|
+
|
|
95
|
+
yaml = YAML.safe_load(File.read(config_path), symbolize_names: true)
|
|
96
|
+
yaml || {}
|
|
97
|
+
rescue StandardError => e
|
|
98
|
+
warn "Failed to load YAML config: #{e.message}"
|
|
99
|
+
{}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# ブックマーク用のYAMLストレージを取得
|
|
103
|
+
# @return [YamlBookmarkStorage] ストレージインスタンス
|
|
104
|
+
def bookmark_storage
|
|
105
|
+
YamlBookmarkStorage.new(YAML_CONFIG_PATH)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# 必要に応じてブックマークをJSONからYAMLに移行
|
|
109
|
+
# @param json_path [String] JSONファイルパス
|
|
110
|
+
# @param yaml_path [String] YAMLファイルパス
|
|
111
|
+
# @return [Boolean] 移行が実行されたかどうか
|
|
112
|
+
def migrate_bookmarks_if_needed(json_path = JSON_BOOKMARKS_PATH, yaml_path = YAML_CONFIG_PATH)
|
|
113
|
+
BookmarkMigrator.migrate(json_path, yaml_path)
|
|
114
|
+
end
|
|
115
|
+
|
|
63
116
|
private
|
|
64
117
|
|
|
65
118
|
def load_config_file
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'task_status'
|
|
4
|
+
|
|
5
|
+
module Rufio
|
|
6
|
+
# 複数のジョブを管理するクラス
|
|
7
|
+
# ジョブの追加、状態追跡、通知連携を行う
|
|
8
|
+
class JobManager
|
|
9
|
+
attr_reader :jobs
|
|
10
|
+
|
|
11
|
+
def initialize(notification_manager: nil)
|
|
12
|
+
@jobs = []
|
|
13
|
+
@notification_manager = notification_manager
|
|
14
|
+
@next_id = 1
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# ジョブを追加
|
|
18
|
+
# @param name [String] ジョブ名
|
|
19
|
+
# @param path [String] 実行ディレクトリ
|
|
20
|
+
# @param command [String] 実行コマンド
|
|
21
|
+
# @return [TaskStatus] 作成されたジョブ
|
|
22
|
+
def add_job(name:, path:, command:)
|
|
23
|
+
job = TaskStatus.new(
|
|
24
|
+
id: @next_id,
|
|
25
|
+
name: name,
|
|
26
|
+
path: path
|
|
27
|
+
)
|
|
28
|
+
# commandを保存(TaskStatusに追加)
|
|
29
|
+
job.instance_variable_set(:@command, command)
|
|
30
|
+
job.define_singleton_method(:command) { @command }
|
|
31
|
+
|
|
32
|
+
@jobs << job
|
|
33
|
+
@next_id += 1
|
|
34
|
+
job
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# IDでジョブを検索
|
|
38
|
+
# @param id [Integer] ジョブID
|
|
39
|
+
# @return [TaskStatus, nil]
|
|
40
|
+
def find_job(id)
|
|
41
|
+
@jobs.find { |job| job.id == id }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# ジョブ数を取得
|
|
45
|
+
# @return [Integer]
|
|
46
|
+
def job_count
|
|
47
|
+
@jobs.size
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# 実行中のジョブ数
|
|
51
|
+
# @return [Integer]
|
|
52
|
+
def running_count
|
|
53
|
+
@jobs.count(&:running?)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# 完了したジョブ数
|
|
57
|
+
# @return [Integer]
|
|
58
|
+
def completed_count
|
|
59
|
+
@jobs.count(&:completed?)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# 失敗したジョブ数
|
|
63
|
+
# @return [Integer]
|
|
64
|
+
def failed_count
|
|
65
|
+
@jobs.count(&:failed?)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# ステータスサマリーを取得
|
|
69
|
+
# @return [Hash] { total:, running:, done:, failed: }
|
|
70
|
+
def status_summary
|
|
71
|
+
{
|
|
72
|
+
total: @jobs.size,
|
|
73
|
+
running: running_count,
|
|
74
|
+
done: completed_count,
|
|
75
|
+
failed: failed_count
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# ジョブをキャンセル
|
|
80
|
+
# @param id [Integer] ジョブID
|
|
81
|
+
# @return [Boolean] キャンセル成功かどうか
|
|
82
|
+
def cancel_job(id)
|
|
83
|
+
job = find_job(id)
|
|
84
|
+
return false unless job
|
|
85
|
+
|
|
86
|
+
job.cancel
|
|
87
|
+
true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# 完了したジョブをクリア
|
|
91
|
+
def clear_completed
|
|
92
|
+
@jobs.reject! { |job| job.completed? || job.cancelled? }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# ステータスバー用のテキストを生成
|
|
96
|
+
# @return [String] "3 jobs: 2 running, 1 done" のような形式
|
|
97
|
+
def status_bar_text
|
|
98
|
+
summary = status_summary
|
|
99
|
+
"#{summary[:total]} jobs: #{summary[:running]} running, #{summary[:done]} done"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# ジョブがあるかどうか
|
|
103
|
+
# @return [Boolean]
|
|
104
|
+
def has_jobs?
|
|
105
|
+
!@jobs.empty?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# 実行中のジョブがあるかどうか
|
|
109
|
+
# @return [Boolean]
|
|
110
|
+
def any_running?
|
|
111
|
+
@jobs.any?(&:running?)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# ジョブ完了時の通知を送信(NotificationManagerと連携)
|
|
115
|
+
# @param job [TaskStatus] 完了したジョブ
|
|
116
|
+
def notify_completion(job)
|
|
117
|
+
return unless @notification_manager
|
|
118
|
+
|
|
119
|
+
type = job.completed? ? :success : :error
|
|
120
|
+
@notification_manager.add(
|
|
121
|
+
job.name,
|
|
122
|
+
type,
|
|
123
|
+
duration: job.duration || 0,
|
|
124
|
+
exit_code: job.exit_code
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rufio
|
|
4
|
+
# ジョブモードのUI管理クラス
|
|
5
|
+
# ジョブ一覧の表示、選択、操作を行う
|
|
6
|
+
class JobMode
|
|
7
|
+
attr_reader :selected_index
|
|
8
|
+
|
|
9
|
+
def initialize(job_manager:)
|
|
10
|
+
@job_manager = job_manager
|
|
11
|
+
@active = false
|
|
12
|
+
@selected_index = 0
|
|
13
|
+
@log_mode = false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# ジョブモードを有効化
|
|
17
|
+
def activate
|
|
18
|
+
@active = true
|
|
19
|
+
@selected_index = 0
|
|
20
|
+
@log_mode = false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# ジョブモードを無効化
|
|
24
|
+
def deactivate
|
|
25
|
+
@active = false
|
|
26
|
+
@log_mode = false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# ジョブモードが有効かどうか
|
|
30
|
+
# @return [Boolean]
|
|
31
|
+
def active?
|
|
32
|
+
@active
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# ログモードが有効かどうか
|
|
36
|
+
# @return [Boolean]
|
|
37
|
+
def log_mode?
|
|
38
|
+
@log_mode
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# ログモードに入る
|
|
42
|
+
def enter_log_mode
|
|
43
|
+
@log_mode = true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# ログモードを終了
|
|
47
|
+
def exit_log_mode
|
|
48
|
+
@log_mode = false
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# 下に移動
|
|
52
|
+
def move_down
|
|
53
|
+
max_index = [@job_manager.job_count - 1, 0].max
|
|
54
|
+
@selected_index = [@selected_index + 1, max_index].min
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# 上に移動
|
|
58
|
+
def move_up
|
|
59
|
+
@selected_index = [@selected_index - 1, 0].max
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# 先頭に移動
|
|
63
|
+
def move_to_top
|
|
64
|
+
@selected_index = 0
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# 末尾に移動
|
|
68
|
+
def move_to_bottom
|
|
69
|
+
@selected_index = [@job_manager.job_count - 1, 0].max
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# 選択中のジョブを取得
|
|
73
|
+
# @return [TaskStatus, nil]
|
|
74
|
+
def selected_job
|
|
75
|
+
return nil if @job_manager.jobs.empty?
|
|
76
|
+
|
|
77
|
+
@job_manager.jobs[@selected_index]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# 選択中のジョブをキャンセル
|
|
81
|
+
# @return [Boolean]
|
|
82
|
+
def cancel_selected_job
|
|
83
|
+
job = selected_job
|
|
84
|
+
return false unless job
|
|
85
|
+
|
|
86
|
+
@job_manager.cancel_job(job.id)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# キー入力を処理
|
|
90
|
+
# @param key [String] 入力されたキー
|
|
91
|
+
# @return [Boolean, Symbol] 処理結果
|
|
92
|
+
def handle_key(key)
|
|
93
|
+
# ログモード中の処理
|
|
94
|
+
if @log_mode
|
|
95
|
+
return handle_log_mode_key(key)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
case key
|
|
99
|
+
when 'j'
|
|
100
|
+
move_down
|
|
101
|
+
true
|
|
102
|
+
when 'k'
|
|
103
|
+
move_up
|
|
104
|
+
true
|
|
105
|
+
when 'g'
|
|
106
|
+
move_to_top
|
|
107
|
+
true
|
|
108
|
+
when 'G'
|
|
109
|
+
move_to_bottom
|
|
110
|
+
true
|
|
111
|
+
when 'x'
|
|
112
|
+
cancel_selected_job
|
|
113
|
+
true
|
|
114
|
+
when ' ' # Space
|
|
115
|
+
enter_log_mode if selected_job
|
|
116
|
+
:show_log
|
|
117
|
+
when "\e" # Escape
|
|
118
|
+
deactivate
|
|
119
|
+
:exit
|
|
120
|
+
else
|
|
121
|
+
false
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
# ログモード中のキー処理
|
|
128
|
+
# @param key [String] 入力されたキー
|
|
129
|
+
# @return [Boolean, Symbol]
|
|
130
|
+
def handle_log_mode_key(key)
|
|
131
|
+
case key
|
|
132
|
+
when "\e" # Escape
|
|
133
|
+
exit_log_mode
|
|
134
|
+
true
|
|
135
|
+
when 'j'
|
|
136
|
+
# ログのスクロール(将来実装)
|
|
137
|
+
true
|
|
138
|
+
when 'k'
|
|
139
|
+
# ログのスクロール(将来実装)
|
|
140
|
+
true
|
|
141
|
+
else
|
|
142
|
+
false
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|