strata-cli 0.1.7 → 0.1.9
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 +25 -0
- data/README.md +24 -1
- data/lib/strata/cli/ai/services/table_generator.rb +35 -20
- data/lib/strata/cli/api/client.rb +23 -63
- data/lib/strata/cli/api/response_error_handler.rb +115 -0
- data/lib/strata/cli/credentials.rb +5 -9
- data/lib/strata/cli/error_reporter.rb +4 -1
- data/lib/strata/cli/generators/datasource.rb +4 -3
- data/lib/strata/cli/generators/group.rb +37 -0
- data/lib/strata/cli/generators/migration.rb +2 -1
- data/lib/strata/cli/generators/project.rb +18 -11
- data/lib/strata/cli/generators/relation.rb +2 -1
- data/lib/strata/cli/generators/table.rb +5 -8
- data/lib/strata/cli/generators/templates/adapters/databricks.yml +4 -3
- data/lib/strata/cli/generators/templates/adapters/snowflake.yml +1 -21
- data/lib/strata/cli/generators/templates/table.table_name.yml +1 -1
- data/lib/strata/cli/generators/test.rb +2 -1
- data/lib/strata/cli/guard.rb +4 -1
- data/lib/strata/cli/helpers/command_context.rb +8 -9
- data/lib/strata/cli/helpers/datasource_helper.rb +1 -1
- data/lib/strata/cli/helpers/description_helper.rb +2 -1
- data/lib/strata/cli/main.rb +15 -3
- data/lib/strata/cli/output.rb +103 -0
- data/lib/strata/cli/sub_commands/audit.rb +4 -3
- data/lib/strata/cli/sub_commands/branch.rb +163 -0
- data/lib/strata/cli/sub_commands/create.rb +1 -2
- data/lib/strata/cli/sub_commands/datasource.rb +13 -4
- data/lib/strata/cli/sub_commands/deploy.rb +14 -13
- data/lib/strata/cli/sub_commands/project.rb +4 -3
- data/lib/strata/cli/sub_commands/table.rb +9 -8
- data/lib/strata/cli/terminal.rb +7 -4
- data/lib/strata/cli/ui/field_editor.rb +21 -27
- data/lib/strata/cli/utils/deployment_monitor.rb +15 -34
- data/lib/strata/cli/utils/git.rb +78 -0
- data/lib/strata/cli/utils/import_manager.rb +4 -1
- data/lib/strata/cli/utils/test_reporter.rb +4 -32
- data/lib/strata/cli/utils/version_checker.rb +4 -8
- data/lib/strata/cli/utils.rb +3 -1
- data/lib/strata/cli/version.rb +1 -1
- data/lib/strata/cli.rb +4 -3
- metadata +4 -2
- data/lib/strata/cli/helpers/color_helper.rb +0 -103
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../guard"
|
|
4
|
+
require_relative "../output"
|
|
5
|
+
require_relative "../api/client"
|
|
6
|
+
require_relative "../utils/git"
|
|
7
|
+
require "time"
|
|
8
|
+
require "tty-prompt"
|
|
9
|
+
|
|
10
|
+
module Strata
|
|
11
|
+
module CLI
|
|
12
|
+
module SubCommands
|
|
13
|
+
class Branch < Thor
|
|
14
|
+
include Guard
|
|
15
|
+
include Output
|
|
16
|
+
|
|
17
|
+
default_command :list
|
|
18
|
+
class_option :environment, aliases: ["e"], type: :string
|
|
19
|
+
|
|
20
|
+
desc "list", "List local and Strata server branches"
|
|
21
|
+
def list
|
|
22
|
+
config = CLI.config.get_for_environment(options[:environment])
|
|
23
|
+
local_branch_names = Utils::Git.local_branches
|
|
24
|
+
server_branches = fetch_server_branches(config)
|
|
25
|
+
|
|
26
|
+
rows = branch_rows(local_branch_names, server_branches)
|
|
27
|
+
if rows.empty?
|
|
28
|
+
print_info("No local or Strata server branches found.")
|
|
29
|
+
else
|
|
30
|
+
rows.each { |row| print_info(row) }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
desc "create BRANCH", "Create and checkout a local git branch"
|
|
35
|
+
def create(branch_name)
|
|
36
|
+
branch_name = Utils::Git.create_and_checkout_branch(branch_name)
|
|
37
|
+
print_success("Created and checked out branch '#{branch_name}'.")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
desc "checkout BRANCH", "Checkout a local git branch"
|
|
41
|
+
def checkout(branch_name)
|
|
42
|
+
branch_name = Utils::Git.checkout_branch(branch_name)
|
|
43
|
+
print_success("Checked out branch '#{branch_name}'.")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
desc "delete", "Delete a branch from Strata server"
|
|
47
|
+
method_option :branch, aliases: ["b"], type: :string, desc: "Branch to delete. Defaults to current git branch."
|
|
48
|
+
def delete
|
|
49
|
+
branch_id = branch_to_delete
|
|
50
|
+
config = CLI.config.get_for_environment(options[:environment])
|
|
51
|
+
validate_delete_configuration(config)
|
|
52
|
+
|
|
53
|
+
return unless confirm_branch_deletion(branch_id)
|
|
54
|
+
|
|
55
|
+
client = API::Client.new(config["server"], config["api_key"])
|
|
56
|
+
server_deleted = client.delete_branch(config["project_id"], branch_id)
|
|
57
|
+
print_warning("Branch '#{branch_id}' was not found on Strata server. Deleting local branch only.") unless server_deleted
|
|
58
|
+
|
|
59
|
+
delete_local_branch(branch_id)
|
|
60
|
+
|
|
61
|
+
print_success("Deleted branch '#{branch_id}'.")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def branch_to_delete
|
|
67
|
+
explicit_branch = options[:branch].to_s.strip
|
|
68
|
+
branch_id = explicit_branch.empty? ? Utils::Git.current_branch.to_s.strip : explicit_branch
|
|
69
|
+
|
|
70
|
+
if branch_id.empty?
|
|
71
|
+
raise Strata::CommandError, "Branch name is required. Use -b BRANCH to specify one."
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if branch_id == "HEAD"
|
|
75
|
+
raise Strata::CommandError, "Cannot determine current branch while in detached HEAD. Use -b BRANCH to specify one."
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
branch_id
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def validate_delete_configuration(config)
|
|
82
|
+
ensure_config_present(config, "server")
|
|
83
|
+
ensure_config_present(config, "api_key")
|
|
84
|
+
ensure_config_present(config, "project_id")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def ensure_config_present(config, key)
|
|
88
|
+
return if config[key] && !config[key].to_s.strip.empty?
|
|
89
|
+
|
|
90
|
+
raise Strata::CommandError, "Missing required configuration: #{key}. Check your project.yml or .strata file."
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def confirm_branch_deletion(branch_id)
|
|
94
|
+
prompt = TTY::Prompt.new
|
|
95
|
+
answer = prompt.ask("Type '#{branch_id}' to delete this branch from Strata:")
|
|
96
|
+
return true if answer.to_s == branch_id
|
|
97
|
+
|
|
98
|
+
print_warning("Branch deletion cancelled.")
|
|
99
|
+
false
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def fetch_server_branches(config)
|
|
103
|
+
unless server_configured?(config)
|
|
104
|
+
print_warning("Server configuration is incomplete. Showing local branches only.")
|
|
105
|
+
return []
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
API::Client.new(config["server"], config["api_key"]).branches(config["project_id"])
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def server_configured?(config)
|
|
112
|
+
%w[server api_key project_id].all? { |key| config[key] && !config[key].to_s.strip.empty? }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def branch_rows(local_branch_names, server_branches)
|
|
116
|
+
local_names = local_branch_names.to_h { |name| [name, true] }
|
|
117
|
+
server_by_uid = server_branches.to_h { |branch| [branch["uid"], branch] }
|
|
118
|
+
current_branch = Utils::Git.git_repo? ? Utils::Git.current_branch : nil
|
|
119
|
+
|
|
120
|
+
(local_names.keys | server_by_uid.keys).sort.map do |branch_name|
|
|
121
|
+
server_branch = server_by_uid[branch_name]
|
|
122
|
+
"#{current_branch_marker(branch_name, current_branch)}(#{branch_location(local_names.key?(branch_name), server_branch)}) - last deployed at: #{format_last_deployed_at(server_branch && server_branch["last_deployed_at"])}"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def current_branch_marker(branch_name, current_branch)
|
|
127
|
+
(current_branch == branch_name) ? "* #{branch_name}" : branch_name
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def branch_location(local, server_branch)
|
|
131
|
+
tags = []
|
|
132
|
+
tags << "local" if local
|
|
133
|
+
tags << "server" if server_branch
|
|
134
|
+
tags.empty? ? "unknown" : tags.join(", ")
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def format_last_deployed_at(value)
|
|
138
|
+
return "" if value.nil? || value.to_s.strip.empty?
|
|
139
|
+
|
|
140
|
+
Time.parse(value.to_s).strftime("%Y-%m-%d %H:%M:%S")
|
|
141
|
+
rescue ArgumentError
|
|
142
|
+
value.to_s
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def delete_local_branch(branch_id)
|
|
146
|
+
case Utils::Git.delete_local_branch(branch_id, fallback_branches: local_delete_fallback_branches)
|
|
147
|
+
when :deleted
|
|
148
|
+
print_info("Deleted local git branch '#{branch_id}'.")
|
|
149
|
+
when :not_found
|
|
150
|
+
print_warning("Local git branch '#{branch_id}' was not found.")
|
|
151
|
+
when :not_git_repo
|
|
152
|
+
print_warning("No local git repository found. Skipped local branch deletion.")
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def local_delete_fallback_branches
|
|
157
|
+
production_branch = CLI.config["production_branch"].to_s.strip
|
|
158
|
+
([production_branch] + %w[main master]).reject(&:empty?).uniq
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -7,7 +7,6 @@ require_relative "../ui/field_editor"
|
|
|
7
7
|
require_relative "../credentials"
|
|
8
8
|
require_relative "../helpers/table_filter"
|
|
9
9
|
require_relative "../ai/services/table_generator"
|
|
10
|
-
require_relative "../helpers/color_helper"
|
|
11
10
|
require_relative "../helpers/prompts"
|
|
12
11
|
require_relative "../helpers/description_helper"
|
|
13
12
|
require_relative "../helpers/command_context"
|
|
@@ -406,7 +405,7 @@ module Strata
|
|
|
406
405
|
|
|
407
406
|
return unless from.strip == to.strip
|
|
408
407
|
|
|
409
|
-
|
|
408
|
+
print_warning("Warning: --from and --to have the same value. Migration will have no effect.")
|
|
410
409
|
end
|
|
411
410
|
|
|
412
411
|
def prompt_migration_hook(operation)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "../guard"
|
|
4
4
|
require_relative "../credentials"
|
|
5
5
|
require_relative "../terminal"
|
|
6
|
+
require_relative "../output"
|
|
6
7
|
require_relative "../utils/git"
|
|
7
8
|
require "tty-prompt"
|
|
8
9
|
require_relative "../helpers/datasource_helper"
|
|
@@ -15,6 +16,7 @@ module Strata
|
|
|
15
16
|
include Thor::Actions
|
|
16
17
|
include Guard
|
|
17
18
|
include Terminal
|
|
19
|
+
include Output
|
|
18
20
|
include DatasourceHelper
|
|
19
21
|
extend Helpers::DescriptionHelper
|
|
20
22
|
|
|
@@ -86,7 +88,7 @@ module Strata
|
|
|
86
88
|
# Collect adapter-specific fields
|
|
87
89
|
adapter_fields(adapter).each do |field, default_value|
|
|
88
90
|
config[field] = if field == "auth_mode"
|
|
89
|
-
|
|
91
|
+
default_value
|
|
90
92
|
elsif %w[ssl azure].include?(field)
|
|
91
93
|
prompt.yes?(" #{field.tr("_", " ").capitalize}?", default: default_value)
|
|
92
94
|
elsif field == "port"
|
|
@@ -103,11 +105,14 @@ module Strata
|
|
|
103
105
|
say "\n✔ Added #{adapter} config to datasources.yml", :green
|
|
104
106
|
|
|
105
107
|
# Automatically collect credentials if required
|
|
106
|
-
creds = Credentials.new(adapter)
|
|
108
|
+
creds = Credentials.new(adapter, datasource_config: config)
|
|
107
109
|
if creds.required?
|
|
108
110
|
say "\n Now let's set up credentials:\n", :yellow
|
|
109
111
|
say " Note: Credentials are stored securely in the local .strata file", :cyan
|
|
110
112
|
say " and are NOT committed to the repository (ensured by .gitignore).", :cyan
|
|
113
|
+
if adapter == "databricks"
|
|
114
|
+
say " Databricks uses OAuth M2M with service principal credentials (client ID + client secret).", :cyan
|
|
115
|
+
end
|
|
111
116
|
say ""
|
|
112
117
|
creds.collect
|
|
113
118
|
creds.write_local(ds_key, self)
|
|
@@ -137,7 +142,7 @@ module Strata
|
|
|
137
142
|
end
|
|
138
143
|
|
|
139
144
|
adapter = datasources[ds_key]["adapter"]
|
|
140
|
-
creds = Credentials.new(adapter)
|
|
145
|
+
creds = Credentials.new(adapter, datasource_config: datasources[ds_key])
|
|
141
146
|
|
|
142
147
|
unless creds.required?
|
|
143
148
|
say "Credentials not required for #{adapter} adapter.", :yellow
|
|
@@ -147,6 +152,9 @@ module Strata
|
|
|
147
152
|
say "\nEnter credentials for #{ds_key}", :red
|
|
148
153
|
say " Note: Credentials are stored securely in the local .strata file", :cyan
|
|
149
154
|
say " and are NOT committed to the repository (ensured by .gitignore).", :cyan
|
|
155
|
+
if adapter == "databricks"
|
|
156
|
+
say " Databricks uses OAuth M2M with service principal credentials (client ID + client secret).", :cyan
|
|
157
|
+
end
|
|
150
158
|
say ""
|
|
151
159
|
creds.collect
|
|
152
160
|
creds.write_local(ds_key, self)
|
|
@@ -343,7 +351,8 @@ module Strata
|
|
|
343
351
|
"host" => "workspace-id.cloud.databricks.com",
|
|
344
352
|
"warehouse" => "warehouse_id",
|
|
345
353
|
"catalog" => "main",
|
|
346
|
-
"schema" => "default"
|
|
354
|
+
"schema" => "default",
|
|
355
|
+
"auth_mode" => "oauth_m2m"
|
|
347
356
|
}
|
|
348
357
|
else
|
|
349
358
|
{}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../guard"
|
|
4
4
|
require_relative "../terminal"
|
|
5
|
-
require_relative "../
|
|
5
|
+
require_relative "../output"
|
|
6
6
|
require_relative "../helpers/project_helper"
|
|
7
7
|
require_relative "../helpers/description_helper"
|
|
8
8
|
require_relative "../api/client"
|
|
@@ -22,6 +22,7 @@ module Strata
|
|
|
22
22
|
class Deploy < Thor
|
|
23
23
|
include Guard
|
|
24
24
|
include Terminal
|
|
25
|
+
include Output
|
|
25
26
|
extend Helpers::DescriptionHelper
|
|
26
27
|
|
|
27
28
|
default_command :deploy
|
|
@@ -57,7 +58,7 @@ module Strata
|
|
|
57
58
|
# If only external imports changed, generate synthetic commit identifier
|
|
58
59
|
if refreshed_imports.any? && local_file_changes.empty?
|
|
59
60
|
import_commit_hash = Utils::ImportManager.generate_import_commit_hash(project_path)
|
|
60
|
-
|
|
61
|
+
print_info("\nExternal imports change found. Proceeding with deployment")
|
|
61
62
|
metadata[:commit] = "imports-#{import_commit_hash}"
|
|
62
63
|
metadata[:commit_message] = "External imports updated: #{refreshed_imports.map do |c|
|
|
63
64
|
File.basename(c[:source])
|
|
@@ -68,7 +69,7 @@ module Strata
|
|
|
68
69
|
|
|
69
70
|
env_display = options[:environment] ? " (#{options[:environment]} environment)" : ""
|
|
70
71
|
server_info = config["server"] ? " (#{config["server"]})" : ""
|
|
71
|
-
|
|
72
|
+
print_info("\nDeploying to branch '#{branch_id}' on Strata server#{env_display}#{server_info}\n")
|
|
72
73
|
|
|
73
74
|
deployment = submit_deployment(config, branch_id, archive_path, metadata)
|
|
74
75
|
|
|
@@ -87,7 +88,7 @@ module Strata
|
|
|
87
88
|
deployment = client.latest_deployment(config["project_id"], branch_id)
|
|
88
89
|
|
|
89
90
|
unless deployment
|
|
90
|
-
|
|
91
|
+
print_info("No deployments found for branch '#{branch_id}'.\n")
|
|
91
92
|
return
|
|
92
93
|
end
|
|
93
94
|
|
|
@@ -331,8 +332,8 @@ module Strata
|
|
|
331
332
|
|
|
332
333
|
if changed_paths.empty? && refreshed_imports.empty?
|
|
333
334
|
unless options[:force]
|
|
334
|
-
|
|
335
|
-
|
|
335
|
+
print_warning("\nNo files changed since last deployment.")
|
|
336
|
+
print_info("To force deploy, run command with --force or -f flag.\n")
|
|
336
337
|
exit(0)
|
|
337
338
|
end
|
|
338
339
|
Utils::Archive.create(project_path, file_overrides: file_overrides)
|
|
@@ -345,7 +346,7 @@ module Strata
|
|
|
345
346
|
change_count = files_to_include.length
|
|
346
347
|
change_count += refreshed_imports.length if refreshed_imports.any?
|
|
347
348
|
|
|
348
|
-
|
|
349
|
+
print_info("Including #{change_count} changed file(s) in archive...\n")
|
|
349
350
|
Utils::Archive.create(project_path, files_to_include: files_to_include, file_overrides: file_overrides)
|
|
350
351
|
end
|
|
351
352
|
else
|
|
@@ -412,7 +413,7 @@ module Strata
|
|
|
412
413
|
message = "Deploying to production branch '#{branch_id}'. Continue? [y/N]"
|
|
413
414
|
return if prompt.yes?(message, default: false)
|
|
414
415
|
|
|
415
|
-
|
|
416
|
+
print_warning("Deployment cancelled.")
|
|
416
417
|
exit(0)
|
|
417
418
|
end
|
|
418
419
|
|
|
@@ -422,7 +423,7 @@ module Strata
|
|
|
422
423
|
project_config = YAML.safe_load_file("project.yml", permitted_classes: [Date, Time], aliases: true) || {}
|
|
423
424
|
project_config["production_branch"] || "main"
|
|
424
425
|
rescue => e
|
|
425
|
-
|
|
426
|
+
print_warning("Failed to load project.yml: #{e.message}") if ENV["DEBUG"]
|
|
426
427
|
"main"
|
|
427
428
|
end
|
|
428
429
|
|
|
@@ -436,11 +437,11 @@ module Strata
|
|
|
436
437
|
return nil unless last_deployment && last_deployment["commit"]
|
|
437
438
|
|
|
438
439
|
commit_hash = last_deployment["commit"]
|
|
439
|
-
|
|
440
|
+
print_info("Found last successful deployment at commit: #{commit_hash[0..7]}...\n")
|
|
440
441
|
commit_hash
|
|
441
442
|
rescue Strata::CommandError
|
|
442
443
|
# If we can't get last deployment (e.g., first deployment), continue with all files
|
|
443
|
-
|
|
444
|
+
print_info("No previous deployment found. Including all files.\n")
|
|
444
445
|
nil
|
|
445
446
|
end
|
|
446
447
|
end
|
|
@@ -477,10 +478,10 @@ module Strata
|
|
|
477
478
|
refreshed = Utils::ImportManager.refresh_external_imports(project_path)
|
|
478
479
|
|
|
479
480
|
if refreshed.any?
|
|
480
|
-
|
|
481
|
+
print_info("\n Skimmed #{refreshed.length} external import(s):")
|
|
481
482
|
refreshed.each do |import|
|
|
482
483
|
filename = File.basename(import[:source])
|
|
483
|
-
|
|
484
|
+
print_info(" • #{filename}")
|
|
484
485
|
end
|
|
485
486
|
end
|
|
486
487
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../guard"
|
|
4
4
|
require_relative "../terminal"
|
|
5
|
-
require_relative "../
|
|
5
|
+
require_relative "../output"
|
|
6
6
|
require_relative "../helpers/project_helper"
|
|
7
7
|
require_relative "../api/client"
|
|
8
8
|
require "yaml"
|
|
@@ -13,6 +13,7 @@ module Strata
|
|
|
13
13
|
class Project < Thor
|
|
14
14
|
include Guard
|
|
15
15
|
include Terminal
|
|
16
|
+
include Output
|
|
16
17
|
|
|
17
18
|
desc "link PROJECT_ID", "Link local project to an existing project on the server"
|
|
18
19
|
def link(project_id)
|
|
@@ -22,13 +23,13 @@ module Strata
|
|
|
22
23
|
project_config = YAML.safe_load_file(project_yml_path, permitted_classes: [Date, Time], aliases: true) || {}
|
|
23
24
|
|
|
24
25
|
if project_config["project_id"] && !project_config["project_id"].to_s.strip.empty?
|
|
25
|
-
|
|
26
|
+
print_warning("Project is already linked to project_id: #{project_config["project_id"]}")
|
|
26
27
|
return
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
return unless Helpers::ProjectHelper.persist_project_id_to_yml(project_id, project_yml_path: project_yml_path)
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
print_info("✓ Project linked successfully. project_id: #{project_id}")
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "../helpers/color_helper"
|
|
4
3
|
require_relative "../helpers/prompts"
|
|
5
4
|
require_relative "../helpers/command_context"
|
|
5
|
+
require_relative "../output"
|
|
6
6
|
|
|
7
7
|
module Strata
|
|
8
8
|
module CLI
|
|
@@ -10,6 +10,7 @@ module Strata
|
|
|
10
10
|
class Table < Thor
|
|
11
11
|
include Guard
|
|
12
12
|
include Terminal
|
|
13
|
+
include Output
|
|
13
14
|
include Prompts
|
|
14
15
|
include Thor::Actions
|
|
15
16
|
include Helpers::CommandContext
|
|
@@ -18,24 +19,24 @@ module Strata
|
|
|
18
19
|
desc "list", "List all semantic models in the project"
|
|
19
20
|
def list
|
|
20
21
|
unless Dir.exist?("models")
|
|
21
|
-
|
|
22
|
+
print_warning(MSG_NO_MODELS_DIR)
|
|
22
23
|
return
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
model_files = Dir.glob("models
|
|
26
|
+
model_files = Dir.glob("models/**/tbl[._]*.yml").sort
|
|
26
27
|
|
|
27
28
|
if model_files.empty?
|
|
28
|
-
|
|
29
|
+
print_warning(MSG_NO_MODELS_FOUND)
|
|
29
30
|
return
|
|
30
31
|
end
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
print_info(MSG_MODELS_LIST_HEADER)
|
|
33
34
|
|
|
34
35
|
model_files.each do |file|
|
|
35
36
|
display_model_item(file)
|
|
36
37
|
end
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
print_info(MSG_MODELS_COUNT % model_files.length)
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
private
|
|
@@ -47,9 +48,9 @@ module Strata
|
|
|
47
48
|
desc = "#{desc[0..50]}..." if desc.length > 50
|
|
48
49
|
|
|
49
50
|
if desc.empty?
|
|
50
|
-
|
|
51
|
+
print_info(" #{name}")
|
|
51
52
|
else
|
|
52
|
-
|
|
53
|
+
print_info(" #{name.ljust(25)} #{desc}")
|
|
53
54
|
end
|
|
54
55
|
end
|
|
55
56
|
end
|
data/lib/strata/cli/terminal.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "tty-spinner"
|
|
|
4
4
|
require "pastel"
|
|
5
5
|
require "tty-table"
|
|
6
6
|
require "io/console"
|
|
7
|
+
require_relative "output"
|
|
7
8
|
|
|
8
9
|
module Strata
|
|
9
10
|
module CLI
|
|
@@ -23,13 +24,15 @@ module Strata
|
|
|
23
24
|
|
|
24
25
|
# Shows a loader while running IO tasks
|
|
25
26
|
# Uses consistent format matching deployment monitor: cyan spinner that transitions to checkmark
|
|
26
|
-
def with_spinner(
|
|
27
|
+
def with_spinner(
|
|
28
|
+
message = "Loading...",
|
|
27
29
|
success_message: "",
|
|
28
30
|
failed_message: "",
|
|
29
31
|
clear: false,
|
|
30
32
|
message_color: :cyan,
|
|
31
33
|
spinner_color: :cyan,
|
|
32
|
-
format: :dots
|
|
34
|
+
format: :dots
|
|
35
|
+
)
|
|
33
36
|
spinner = create_spinner(message,
|
|
34
37
|
message_color: message_color,
|
|
35
38
|
spinner_color: spinner_color,
|
|
@@ -39,10 +42,10 @@ module Strata
|
|
|
39
42
|
|
|
40
43
|
begin
|
|
41
44
|
result = yield
|
|
42
|
-
spinner.success(success_message.empty? ? "" :
|
|
45
|
+
spinner.success(success_message.empty? ? "" : Output.format(:success, success_message))
|
|
43
46
|
result
|
|
44
47
|
rescue => e
|
|
45
|
-
spinner.error(failed_message.empty? ? "" :
|
|
48
|
+
spinner.error(failed_message.empty? ? "" : Output.format(:error, failed_message))
|
|
46
49
|
raise e
|
|
47
50
|
end
|
|
48
51
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "tty-prompt"
|
|
4
4
|
require "tty-table"
|
|
5
5
|
require "pastel"
|
|
6
|
-
require_relative "../
|
|
6
|
+
require_relative "../output"
|
|
7
7
|
|
|
8
8
|
module Strata
|
|
9
9
|
module CLI
|
|
@@ -77,21 +77,19 @@ module Strata
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def display_table
|
|
80
|
-
|
|
81
|
-
puts colors.highlight("\n Field Editor - Review & Edit Generated Fields\n")
|
|
80
|
+
puts Output.format(:highlight, "\n Field Editor - Review & Edit Generated Fields\n")
|
|
82
81
|
|
|
83
82
|
rows = @fields.each_with_index.map do |field, idx|
|
|
84
|
-
indicator = (idx == @selected_index) ?
|
|
85
|
-
status = field[:skipped] ?
|
|
83
|
+
indicator = (idx == @selected_index) ? Output.format(:selected, "❯") : " "
|
|
84
|
+
status = field[:skipped] ? Output.format(:disabled, "[SKIP]") : ""
|
|
86
85
|
|
|
87
|
-
name_cell = field[:skipped] ?
|
|
86
|
+
name_cell = field[:skipped] ? Output.format(:disabled, field[:name]) : field[:name]
|
|
88
87
|
desc_cell = if field[:skipped]
|
|
89
|
-
|
|
90
|
-
25))
|
|
88
|
+
Output.format(:disabled, truncate(field[:description], 25))
|
|
91
89
|
else
|
|
92
90
|
truncate(field[:description], 25)
|
|
93
91
|
end
|
|
94
|
-
expr_cell = field[:skipped] ?
|
|
92
|
+
expr_cell = field[:skipped] ? Output.format(:disabled, field[:expression]) : field[:expression]
|
|
95
93
|
|
|
96
94
|
[
|
|
97
95
|
"#{indicator} #{status}",
|
|
@@ -112,14 +110,13 @@ module Strata
|
|
|
112
110
|
end
|
|
113
111
|
|
|
114
112
|
def display_help
|
|
115
|
-
colors = ColorHelper
|
|
116
113
|
puts ""
|
|
117
|
-
puts " #{
|
|
118
|
-
"#{
|
|
119
|
-
"#{
|
|
120
|
-
"#{
|
|
121
|
-
"#{
|
|
122
|
-
"#{
|
|
114
|
+
puts " #{Output.format(:info, "↑/↓")} Navigate " \
|
|
115
|
+
"#{Output.format(:success, "[Enter]")} Edit " \
|
|
116
|
+
"#{Output.format(:warning, "[S]")}kip " \
|
|
117
|
+
"#{Output.format(:primary, "[P]")}rompt " \
|
|
118
|
+
"#{Output.format(:secondary, "[C]")}onfirm " \
|
|
119
|
+
"#{Output.format(:error, "[Q/Esc]")}uit"
|
|
123
120
|
puts ""
|
|
124
121
|
end
|
|
125
122
|
|
|
@@ -129,10 +126,9 @@ module Strata
|
|
|
129
126
|
|
|
130
127
|
def edit_current_field
|
|
131
128
|
field = @fields[@selected_index]
|
|
132
|
-
colors = ColorHelper
|
|
133
129
|
|
|
134
|
-
puts
|
|
135
|
-
puts
|
|
130
|
+
puts Output.format(:highlight, "\n Editing: #{field[:name]}\n")
|
|
131
|
+
puts Output.format(:dim, " (Press Ctrl+C or type 'back' to go back without saving)\n")
|
|
136
132
|
|
|
137
133
|
begin
|
|
138
134
|
field[:name] = prompt.ask(" Field Name:", default: field[:name])
|
|
@@ -170,20 +166,18 @@ module Strata
|
|
|
170
166
|
end
|
|
171
167
|
|
|
172
168
|
def handle_prompt_mode
|
|
173
|
-
require_relative "../ai/services/
|
|
169
|
+
require_relative "../ai/services/table_generator"
|
|
174
170
|
require_relative "../terminal"
|
|
175
|
-
generator = AI::Services::
|
|
176
|
-
|
|
177
|
-
colors = ColorHelper
|
|
171
|
+
generator = AI::Services::TableGenerator.new
|
|
178
172
|
|
|
179
173
|
unless generator.ai_available?
|
|
180
|
-
puts
|
|
174
|
+
puts Output.format(:warning, "\n AI is not available. Configure AI in .strata file.")
|
|
181
175
|
sleep 1.5
|
|
182
176
|
return
|
|
183
177
|
end
|
|
184
178
|
|
|
185
179
|
unless @table_context
|
|
186
|
-
puts
|
|
180
|
+
puts Output.format(:warning, "\n Prompt mode requires table context.")
|
|
187
181
|
sleep 1.5
|
|
188
182
|
return
|
|
189
183
|
end
|
|
@@ -208,11 +202,11 @@ module Strata
|
|
|
208
202
|
@selected_index = 0
|
|
209
203
|
# No message needed - table will refresh and show new fields
|
|
210
204
|
else
|
|
211
|
-
puts
|
|
205
|
+
puts Output.format(:warning, " No changes - keeping current fields")
|
|
212
206
|
sleep 1
|
|
213
207
|
end
|
|
214
208
|
rescue => e
|
|
215
|
-
puts
|
|
209
|
+
puts Output.format(:error, "\n Error: #{e.message}")
|
|
216
210
|
sleep 2
|
|
217
211
|
end
|
|
218
212
|
end
|