strata-cli 0.1.3.beta → 0.1.4.beta
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 +11 -0
- data/README.md +3 -2
- data/lib/strata/cli/ai/services/table_generator.rb +8 -4
- data/lib/strata/cli/configuration.rb +1 -2
- data/lib/strata/cli/generators/table.rb +5 -0
- data/lib/strata/cli/generators/templates/table.table_name.yml +6 -0
- data/lib/strata/cli/helpers/datasource_helper.rb +17 -0
- data/lib/strata/cli/helpers/project_helper.rb +28 -0
- data/lib/strata/cli/sub_commands/datasource.rb +3 -0
- data/lib/strata/cli/sub_commands/deploy.rb +42 -13
- data/lib/strata/cli/ui/field_editor.rb +4 -0
- data/lib/strata/cli/utils/deployment_monitor.rb +1 -1
- data/lib/strata/cli/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2ea80c6d9e3c9d9db1f592e8fc07470b23fecafe6a31052aecd69b948f1bdec7
|
|
4
|
+
data.tar.gz: 57af64d1411a970716f5be79db6ac93b9c54b4f76c0f0dd1265a372ff1c28607
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4de4eb2ce818ce0fe851b79f3fe0d82b2540257246638052c034506d605256bd98ca3cbe7a61dac0e4b240258f39bd6b4cadfeb3681a59038851d2257e68266
|
|
7
|
+
data.tar.gz: 9db418cf2a36236856cafda813885684ad3e9a558ab4bd44ce60f52d57a202f77124b69270b044fd940ef58d95db3f6edfdb9ca36efbaa078e073bc20f6cb68a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.1.4.beta] - 2026-02-26
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Table generator**: Support synonyms for fields in generated tables.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Server configuration**: Refactored server config handling and improved `project.yml` persistence.
|
|
12
|
+
- **Datasource helper**: Read-only mode support for file-based databases.
|
|
13
|
+
|
|
3
14
|
## [0.1.3.beta] - 2026-01-28
|
|
4
15
|
|
|
5
16
|
### Added
|
data/README.md
CHANGED
|
@@ -440,8 +440,9 @@ Release a new version:
|
|
|
440
440
|
|
|
441
441
|
1. Update version in `lib/strata/cli/version.rb`
|
|
442
442
|
2. Update `CHANGELOG.md` with the new version and changes
|
|
443
|
-
3. Commit
|
|
444
|
-
4. Run
|
|
443
|
+
3. Commit and merge to `master`: `git add lib/strata/cli/version.rb CHANGELOG.md && git commit -m "Bump version to X.Y.Z"` (then open a PR and merge)
|
|
444
|
+
4. **Via GitHub Actions (recommended):** In the repo go to **Actions → Release → Run workflow**, choose the branch (e.g. `master`), optionally enable "Build and test only" for a dry run, then **Run workflow**. Ensure the repo secret `RUBYGEMS_API_KEY` is set (RubyGems API key with push permission).
|
|
445
|
+
5. **Via console:** From a checkout of `master` after the version-bump merge, run `bundle exec rake release` (builds gem, creates git tag, pushes tag and gem to RubyGems).
|
|
445
446
|
|
|
446
447
|
## Contributing
|
|
447
448
|
|
|
@@ -20,6 +20,9 @@ module Strata
|
|
|
20
20
|
- schema_type: "dimension" for categorical/text, "measure" for numeric aggregations
|
|
21
21
|
- data_type: string, integer, bigint, decimal, date, date_time, boolean
|
|
22
22
|
- expression: SQL expression (for measures include aggregation like "sum(amount)")
|
|
23
|
+
- synonyms: Array of 2-4 alternative names users might use to refer to this field.
|
|
24
|
+
These help AI search and natural language queries find the right field.
|
|
25
|
+
Example: "Customer ID" → ["cust id", "client id", "buyer id"]
|
|
23
26
|
|
|
24
27
|
Output ONLY valid JSON array, no explanations.
|
|
25
28
|
PROMPT
|
|
@@ -120,8 +123,8 @@ module Strata
|
|
|
120
123
|
|
|
121
124
|
#{column_list}#{context_section}#{user_context_section}
|
|
122
125
|
|
|
123
|
-
Return JSON array with objects containing: name, description, schema_type, data_type, expression
|
|
124
|
-
Example: [{"name": "Order ID", "description": "Primary key", "schema_type": "dimension", "data_type": "bigint", "expression": "id"}]
|
|
126
|
+
Return JSON array with objects containing: name, description, schema_type, data_type, expression, synonyms
|
|
127
|
+
Example: [{"name": "Order ID", "description": "Primary key", "schema_type": "dimension", "data_type": "bigint", "expression": "id", "synonyms": ["order number", "order key"]}]
|
|
125
128
|
PROMPT
|
|
126
129
|
end
|
|
127
130
|
|
|
@@ -153,7 +156,7 @@ module Strata
|
|
|
153
156
|
#{column_list}#{context_section}
|
|
154
157
|
|
|
155
158
|
Based on the user's request, regenerate or modify the field definitions.
|
|
156
|
-
Return JSON array with objects containing: name, description, schema_type, data_type, expression
|
|
159
|
+
Return JSON array with objects containing: name, description, schema_type, data_type, expression, synonyms
|
|
157
160
|
PROMPT
|
|
158
161
|
end
|
|
159
162
|
|
|
@@ -207,7 +210,8 @@ module Strata
|
|
|
207
210
|
description: "",
|
|
208
211
|
schema_type: is_measure ? "measure" : "dimension",
|
|
209
212
|
data_type: map_data_type(type),
|
|
210
|
-
expression: is_measure ? "sum(#{name})" : name
|
|
213
|
+
expression: is_measure ? "sum(#{name})" : name,
|
|
214
|
+
synonyms: []
|
|
211
215
|
}
|
|
212
216
|
end
|
|
213
217
|
end
|
|
@@ -108,6 +108,11 @@ module Strata
|
|
|
108
108
|
"data_type" => normalized[:data_type]
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
synonyms = normalized[:synonyms]
|
|
112
|
+
if synonyms.is_a?(Array) && !synonyms.empty?
|
|
113
|
+
field_hash["synonyms"] = synonyms
|
|
114
|
+
end
|
|
115
|
+
|
|
111
116
|
# Build expression with proper nested format
|
|
112
117
|
expr = normalized[:expression]
|
|
113
118
|
field_hash["expression"] = {
|
|
@@ -96,6 +96,12 @@ fields:
|
|
|
96
96
|
# array: true|false (optional)
|
|
97
97
|
# sql: my_field_column (Required)
|
|
98
98
|
#
|
|
99
|
+
# # Optional: Alternative names for this field. Helps AI and search
|
|
100
|
+
# # find this field when users refer to it by different names.
|
|
101
|
+
# synonyms:
|
|
102
|
+
# - alt name one
|
|
103
|
+
# - alt name two
|
|
104
|
+
#
|
|
99
105
|
# # Optional: Exclude certain dimnesions from the group by/filter
|
|
100
106
|
# exclusion_type: exclude|exclude_all_except|exclude_all
|
|
101
107
|
# exclusions: # (Required when exclusion type is set and isnt exclude_all)
|
|
@@ -42,9 +42,26 @@ module Strata
|
|
|
42
42
|
config = ds_config(ds_key).merge(Credentials.fetch(ds_key))
|
|
43
43
|
adapter_sym = config["adapter"].to_sym
|
|
44
44
|
ensure_adapter_driver_gems!(adapter_sym)
|
|
45
|
+
|
|
46
|
+
# CLI only performs read operations (test, tables, metadata).
|
|
47
|
+
# Use read-only mode for file-based databases to avoid lock conflicts.
|
|
48
|
+
apply_readonly_mode(adapter_sym, config)
|
|
49
|
+
|
|
45
50
|
DWH.create(adapter_sym, config)
|
|
46
51
|
end
|
|
47
52
|
|
|
53
|
+
def apply_readonly_mode(adapter_sym, config)
|
|
54
|
+
case adapter_sym
|
|
55
|
+
when :duckdb
|
|
56
|
+
config["duck_config"] ||= {}
|
|
57
|
+
config["duck_config"]["access_mode"] = "READ_ONLY"
|
|
58
|
+
when :sqlite
|
|
59
|
+
config["readonly"] = true
|
|
60
|
+
end
|
|
61
|
+
# Client-server databases (postgres, mysql, snowflake, etc.)
|
|
62
|
+
# don't need special handling - no file lock issues
|
|
63
|
+
end
|
|
64
|
+
|
|
48
65
|
def ensure_adapter_driver_gems!(adapter_sym)
|
|
49
66
|
required = ADAPTER_DRIVER_GEMS.fetch(adapter_sym, [])
|
|
50
67
|
return if required.empty?
|
|
@@ -46,6 +46,34 @@ module Strata
|
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
# Persists server URL to project.yml (updates existing or appends)
|
|
50
|
+
def persist_server_to_project_yml(server, project_yml_path: "project.yml")
|
|
51
|
+
return false unless server && !server.to_s.strip.empty?
|
|
52
|
+
return false unless File.exist?(project_yml_path)
|
|
53
|
+
|
|
54
|
+
project_yml_content = File.read(project_yml_path)
|
|
55
|
+
|
|
56
|
+
begin
|
|
57
|
+
if /^server:\s*.+$/m.match?(project_yml_content)
|
|
58
|
+
updated_content = project_yml_content.gsub(/^(\s*)server:\s*.+$/, "\\1server: #{server}")
|
|
59
|
+
File.write(project_yml_path, updated_content)
|
|
60
|
+
else
|
|
61
|
+
File.open(project_yml_path, "a") do |f|
|
|
62
|
+
f.puts "\n" unless project_yml_content.end_with?("\n")
|
|
63
|
+
f.puts "server: #{server}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
true
|
|
68
|
+
rescue Errno::EACCES => e
|
|
69
|
+
raise Strata::CommandError, "Permission denied writing to #{project_yml_path}: #{e.message}"
|
|
70
|
+
rescue Errno::ENOSPC => e
|
|
71
|
+
raise Strata::CommandError, "Disk full: #{e.message}"
|
|
72
|
+
rescue => e
|
|
73
|
+
raise Strata::CommandError, "Failed to write to #{project_yml_path}: #{e.message}"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
49
77
|
# Persists git URL to project.yml file if missing
|
|
50
78
|
def persist_git_url_if_missing(project_yml_path: "project.yml")
|
|
51
79
|
return false unless File.exist?(project_yml_path)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require_relative "../guard"
|
|
2
2
|
require_relative "../credentials"
|
|
3
3
|
require_relative "../terminal"
|
|
4
|
+
require_relative "../utils/git"
|
|
4
5
|
require "tty-prompt"
|
|
5
6
|
require_relative "../helpers/datasource_helper"
|
|
6
7
|
require_relative "../helpers/description_helper"
|
|
@@ -369,6 +370,8 @@ module Strata
|
|
|
369
370
|
strata_file = Configuration::STRATA_CONFIG_FILE
|
|
370
371
|
File.chmod(0o600, strata_file) if File.exist?(strata_file)
|
|
371
372
|
|
|
373
|
+
Utils::Git.commit_file(Configuration::STRATA_CONFIG_FILE, "[Strata-CLI] Add AI config to .strata", Dir.pwd) if Utils::Git.git_repo?
|
|
374
|
+
|
|
372
375
|
say "\n✔ AI configured with #{provider}", :green
|
|
373
376
|
say " Note: API key is stored securely in .strata (not committed to repo)", :cyan
|
|
374
377
|
end
|
|
@@ -122,7 +122,7 @@ module Strata
|
|
|
122
122
|
|
|
123
123
|
def validate_deployment_configuration(config)
|
|
124
124
|
ensure_api_key(config)
|
|
125
|
-
|
|
125
|
+
ensure_server(config)
|
|
126
126
|
ensure_git_url_populated
|
|
127
127
|
ensure_project_id(config)
|
|
128
128
|
end
|
|
@@ -133,14 +133,36 @@ module Strata
|
|
|
133
133
|
config["api_key"] = collect_api_key_interactively
|
|
134
134
|
end
|
|
135
135
|
|
|
136
|
-
def
|
|
137
|
-
|
|
138
|
-
return if server_url && !server_url.to_s.strip.empty?
|
|
136
|
+
def ensure_server(config)
|
|
137
|
+
return if config["server"] && !config["server"].to_s.strip.empty?
|
|
139
138
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
config["server"] = collect_server_interactively
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def collect_server_interactively
|
|
143
|
+
prompt = TTY::Prompt.new
|
|
144
|
+
|
|
145
|
+
say "\nServer URL not found (not in project.yml)", :yellow
|
|
146
|
+
say "You can add it to project.yml or enter it now here\n", :cyan
|
|
147
|
+
|
|
148
|
+
server = prompt.ask("Enter Strata server URL:") do |q|
|
|
149
|
+
q.required true
|
|
150
|
+
q.validate(/\S+/, "Server URL cannot be empty")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
server = server.strip.sub(%r{/\z}, "") # normalize: remove trailing slash
|
|
154
|
+
|
|
155
|
+
with_spinner("Saving server URL to project.yml") do
|
|
156
|
+
save_server_to_project_yml(server)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
server
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def save_server_to_project_yml(server)
|
|
163
|
+
Helpers::ProjectHelper.persist_server_to_project_yml(server, project_yml_path: File.join(project_path, Configuration::PROJECT_CONFIG_FILE))
|
|
164
|
+
CLI.config.reload!
|
|
165
|
+
Utils::Git.commit_file(Configuration::PROJECT_CONFIG_FILE, "[Strata-CLI] Add server URL to project.yml", project_path) if Utils::Git.git_repo?
|
|
144
166
|
end
|
|
145
167
|
|
|
146
168
|
def ensure_git_url_populated
|
|
@@ -182,37 +204,43 @@ module Strata
|
|
|
182
204
|
save_api_key_to_strata(api_key)
|
|
183
205
|
end
|
|
184
206
|
|
|
207
|
+
Utils::Git.commit_file(Configuration::STRATA_CONFIG_FILE, "[Strata-CLI] Save API key to .strata", project_path) if Utils::Git.git_repo?
|
|
208
|
+
|
|
185
209
|
api_key
|
|
186
210
|
end
|
|
187
211
|
|
|
188
212
|
def save_api_key_to_strata(api_key)
|
|
213
|
+
save_key_to_strata("api_key", api_key)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def save_key_to_strata(key, value)
|
|
189
217
|
strata_file = Configuration::STRATA_CONFIG_FILE
|
|
190
218
|
|
|
191
219
|
# Read existing content if file exists
|
|
192
220
|
existing_content = File.exist?(strata_file) ? File.read(strata_file) : ""
|
|
193
221
|
|
|
194
222
|
begin
|
|
195
|
-
if
|
|
196
|
-
updated_content = existing_content.gsub(/^(\s*)
|
|
223
|
+
if /^#{Regexp.escape(key)}:\s*.+$/m.match?(existing_content)
|
|
224
|
+
updated_content = existing_content.gsub(/^(\s*)#{Regexp.escape(key)}:\s*.+$/, "\\1#{key}: #{value}")
|
|
197
225
|
File.write(strata_file, updated_content)
|
|
198
226
|
else
|
|
199
227
|
File.open(strata_file, "a") do |f|
|
|
200
228
|
f.puts "\n" unless existing_content.empty? || existing_content.end_with?("\n")
|
|
201
|
-
f.puts "
|
|
229
|
+
f.puts "#{key}: #{value}"
|
|
202
230
|
end
|
|
203
231
|
end
|
|
204
232
|
|
|
205
233
|
# Set restrictive permissions (read/write for owner only)
|
|
206
234
|
File.chmod(0o600, strata_file) if File.exist?(strata_file)
|
|
207
235
|
|
|
208
|
-
# Reload config to pick up the new
|
|
236
|
+
# Reload config to pick up the new value
|
|
209
237
|
CLI.config.reload!
|
|
210
238
|
rescue Errno::EACCES => e
|
|
211
239
|
raise Strata::CommandError, "Permission denied writing to #{strata_file}: #{e.message}"
|
|
212
240
|
rescue Errno::ENOSPC => e
|
|
213
241
|
raise Strata::CommandError, "Disk full: #{e.message}"
|
|
214
242
|
rescue => e
|
|
215
|
-
raise Strata::CommandError, "Failed to write
|
|
243
|
+
raise Strata::CommandError, "Failed to write #{key} to #{strata_file}: #{e.message}"
|
|
216
244
|
end
|
|
217
245
|
end
|
|
218
246
|
|
|
@@ -405,6 +433,7 @@ module Strata
|
|
|
405
433
|
with_spinner("Persisting project ID to project.yml") do
|
|
406
434
|
Helpers::ProjectHelper.persist_project_id_to_yml(project_id)
|
|
407
435
|
end
|
|
436
|
+
Utils::Git.commit_file(Configuration::PROJECT_CONFIG_FILE, "[Strata-CLI] Persist project ID to project.yml", project_path) if Utils::Git.git_repo?
|
|
408
437
|
end
|
|
409
438
|
|
|
410
439
|
def refresh_external_imports
|
|
@@ -143,6 +143,10 @@ module Strata
|
|
|
143
143
|
return if field[:expression] == "back"
|
|
144
144
|
|
|
145
145
|
field[:schema_type] = prompt.select(" Type:", %w[dimension measure], default: field[:schema_type])
|
|
146
|
+
|
|
147
|
+
current_synonyms = (field[:synonyms] || []).join(", ")
|
|
148
|
+
synonyms_input = prompt.ask(" Synonyms (comma-separated):", default: current_synonyms)
|
|
149
|
+
field[:synonyms] = synonyms_input.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
146
150
|
rescue TTY::Reader::InputInterrupt
|
|
147
151
|
# User pressed Ctrl+C, go back without saving
|
|
148
152
|
nil
|
|
@@ -127,7 +127,7 @@ module Strata
|
|
|
127
127
|
rescue Interrupt
|
|
128
128
|
stop_all_spinners
|
|
129
129
|
say "\n\n Monitoring interrupted. Deployment continues in background.", ColorHelper.warning
|
|
130
|
-
say "
|
|
130
|
+
say " Check server for deployment status with command `strata deploy status`.\n", ColorHelper.info
|
|
131
131
|
nil
|
|
132
132
|
rescue => e
|
|
133
133
|
stop_all_spinners
|
data/lib/strata/cli/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: strata-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4.beta
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ajo Abraham
|
|
@@ -330,7 +330,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
330
330
|
- !ruby/object:Gem::Version
|
|
331
331
|
version: '0'
|
|
332
332
|
requirements: []
|
|
333
|
-
rubygems_version: 3.6.
|
|
333
|
+
rubygems_version: 3.6.7
|
|
334
334
|
specification_version: 4
|
|
335
335
|
summary: Command-line interface for the Strata Semantic Analytics System
|
|
336
336
|
test_files: []
|