strata-cli 0.1.18 → 0.1.20

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e13d7f619d5b03f2c0ab353c8064fe5f1c17e97d8a3bc63d0d32abb11da628de
4
- data.tar.gz: '095c483ea8913658de1d52dbe33bd71491b04b88f91f6e5c957417760bd1db1b'
3
+ metadata.gz: 28b84442e2989e729c2f9241c4af084eed8b346f62267d00c7bc27f3a4cb7d5d
4
+ data.tar.gz: b3e436e5055ea145a030bd88e27fb7b11ec1f6965886a65cb94aeee26ba34f5f
5
5
  SHA512:
6
- metadata.gz: 1444ff06c0d6ad24303c6fec60c925fda1fffa17513cb2c8c8e7a2071af0f070181c882d58f832e1fca088c6601168140b0557a20106a0e23513da47173a0302
7
- data.tar.gz: e952da251f4c0562fcc1f26be54f84245e046ed3041374e5efb17faf4b293a903db3c573142ff43c6efd290306252f2ad37cfce27ec27f0a824b6f6485379bc6
6
+ metadata.gz: 022cb33cdb574fd26548b9239ea39520e196f019a5f5d1808e84acf19e15f22a92d7acf550b70d729a799d4a389e48cd66950fa616809afbb96f569f87c00720
7
+ data.tar.gz: f3bd0b00b86ddae87a55262f15d49665606fde89211d3bd2fc5ec9ca33233b998253e5f949d170052a3c8d7ee210c5f6187ed38ed9603487cd5c218fdd9ed89b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.20] - 2026-09-30
4
+
5
+ ### Changed
6
+
7
+ - **File-based secrets stored as contents**: `strata ds auth` now reads a BigQuery service account keyfile and saves only its `client_email` and `private_key`, and reads a Snowflake key-pair private key file and saves the PEM text, so a remote Strata server never needs the file. `.strata` blocks are written with YAML serialisation to hold multi-line values. Requires dwh 0.6.2 and a server with encrypted `password`/`private_key` columns.
8
+ - **`strata ds auth --remote` removed**: the flag was documented but never implemented; credentials reach the server through `strata deploy`.
9
+
10
+ ### Fixed
11
+
12
+ - `strata init -d ADAPTER` wrote a bare `ADAPTER:` node with no config, which then broke `ds list`, `ds auth`, and `audit`. It now renders the commented adapter template again, and templates were added for sqlite, redshift, clickhouse, and bigquery.
13
+
14
+ ## [0.1.19] - 2026-09-30
15
+
16
+ ### Added
17
+
18
+ - **BigQuery datasource**: `strata datasource add bigquery` prompts for `project_id` and `dataset`; `strata ds auth` stores a service account `keyfile` path (Application Default Credentials when left blank). Requires dwh 0.6.0.
19
+
3
20
  ## [0.1.18] - 2026-09-26
4
21
 
5
22
  ### Changed
data/README.md CHANGED
@@ -364,6 +364,8 @@ ai_api_key: your-ai-key
364
364
 
365
365
  **Security:** The `.strata` file automatically has restrictive permissions (0600) for security.
366
366
 
367
+ File-based secrets are stored as contents, not paths: a BigQuery keyfile becomes `client_email` + `private_key`, and a Snowflake key-pair `private_key` holds the PEM text. Credentials travel to the server with each deploy and are stored encrypted there.
368
+
367
369
  ### Authentication
368
370
 
369
371
  You don't need to create an API key manually. When no key is configured, `strata deploy` (and `strata project sample`) asks how you'd like to authenticate:
@@ -434,6 +436,15 @@ my_databricks:
434
436
 
435
437
  Use service principal credentials and run `strata ds auth my_databricks` to save `oauth_client_id` and `oauth_client_secret` in `.strata`.
436
438
 
439
+ ```yaml
440
+ my_bigquery:
441
+ adapter: bigquery
442
+ project_id: my-gcp-project
443
+ dataset: analytics
444
+ ```
445
+
446
+ Run `strata ds auth my_bigquery` and give the service account JSON keyfile path: only its `client_email` and `private_key` are saved in `.strata`, so the server never needs the file.
447
+
437
448
  ## Supported Data Warehouse Adapters
438
449
 
439
450
  - **PostgreSQL** - Full support
@@ -448,6 +459,7 @@ Use service principal credentials and run `strata ds auth my_databricks` to save
448
459
  - **Databricks** - Databricks SQL warehouse support
449
460
  - **ClickHouse** - ClickHouse HTTP/native SQL support
450
461
  - **SQLite** - SQLite database support
462
+ - **BigQuery** - Google BigQuery support (service account key)
451
463
 
452
464
  ## Security
453
465
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "yaml"
4
+ require "json"
4
5
  require "tty-prompt"
5
6
 
6
7
  module Strata
@@ -39,7 +40,7 @@ module Strata
39
40
  credentials["personal_access_token"] = @prompt.ask("Enter Personal Access Token:")
40
41
  when "kp"
41
42
  credentials["username"] = @prompt.ask("Enter Username:")
42
- credentials["private_key"] = @prompt.ask("Enter Private Key Absolute Path:")
43
+ credentials["private_key"] = read_secret_file("Private key path (the PEM contents are stored):")
43
44
  end
44
45
  when "athena"
45
46
  credentials["access_key_id"] = @prompt.ask("AWS Access Key ID:")
@@ -50,6 +51,8 @@ module Strata
50
51
  credentials["oauth_client_secret"] = @prompt.ask("OAuth Client Secret:")
51
52
  when "clickhouse"
52
53
  credentials["password"] = @prompt.mask("Enter Password (leave blank if none):")
54
+ when "bigquery"
55
+ credentials.merge!(bigquery_service_account)
53
56
  else
54
57
  if required?
55
58
  unless %w[postgres mysql trino sqlserver].include?(adapter)
@@ -68,12 +71,11 @@ module Strata
68
71
 
69
72
  def write_local(ds_key, context)
70
73
  context.gsub_file Configuration::STRATA_CONFIG_FILE, Utils.yaml_block_matcher(ds_key), ""
71
- creds = "#{ds_key}:"
72
- credentials.each do |key, val|
73
- creds << "\n #{key}: #{val}" unless val.nil? || val.empty?
74
- end
74
+ values = credentials.reject { |_, val| val.nil? || val.to_s.empty? }
75
75
 
76
- context.append_to_file Configuration::STRATA_CONFIG_FILE, creds
76
+ # YAML.dump so multi-line values (PEM keys) become indented block scalars
77
+ # that yaml_block_matcher can still find and replace on the next auth.
78
+ context.append_to_file Configuration::STRATA_CONFIG_FILE, YAML.dump({ds_key => values}).delete_prefix("---\n")
77
79
 
78
80
  # Set restrictive permissions (read/write for owner only)
79
81
  strata_file = Configuration::STRATA_CONFIG_FILE
@@ -81,6 +83,43 @@ module Strata
81
83
  rescue Errno::EACCES => e
82
84
  raise Strata::CommandError, "Permission denied setting permissions on #{strata_file}: #{e.message}"
83
85
  end
86
+
87
+ private
88
+
89
+ # Asks for a file path and returns the file's contents so the server never
90
+ # needs the file itself. Blank returns nil; an answer that is not a path is
91
+ # kept as typed, which allows pasting the value directly.
92
+ def read_secret_file(question)
93
+ answer = @prompt.ask(question).to_s.strip
94
+ return nil if answer.empty?
95
+
96
+ File.exist?(answer) ? File.read(answer) : answer
97
+ end
98
+
99
+ # Reads the Google service account JSON once and keeps only the two fields
100
+ # needed to authenticate. The server has no other way to reach BigQuery,
101
+ # so without the file nothing is saved and the user is told to come back.
102
+ def bigquery_service_account
103
+ path = @prompt.ask("Service account keyfile path:").to_s.strip
104
+ if path.empty?
105
+ say " No keyfile given, so no BigQuery credentials were saved. Run `strata ds auth <key>` with the service account keyfile before deploying.", :yellow
106
+ return {}
107
+ end
108
+
109
+ key = JSON.parse(File.read(path))
110
+ unless key["type"] == "service_account" && key["client_email"] && key["private_key"]
111
+ raise Strata::CommandError, "#{path} is not a service account keyfile (needs type, client_email and private_key)"
112
+ end
113
+
114
+ expected = @datasource_config["project_id"]
115
+ if expected && key["project_id"] && expected != key["project_id"]
116
+ say " Warning: keyfile project '#{key["project_id"]}' differs from datasources.yml project_id '#{expected}'", :yellow
117
+ end
118
+
119
+ key.slice("client_email", "private_key")
120
+ rescue Errno::ENOENT, JSON::ParserError => e
121
+ raise Strata::CommandError, "Could not read service account keyfile #{path}: #{e.message}"
122
+ end
84
123
  end
85
124
  end
86
125
  end
@@ -18,4 +18,4 @@ Examples:
18
18
 
19
19
  strata datasource add snowflake # Directly add Snowflake datasource
20
20
 
21
- Supported adapters: postgres, mysql, sqlserver, snowflake, athena, trino, duckdb, druid, clickhouse, databricks, redshift, sqlite
21
+ Supported adapters: postgres, mysql, sqlserver, snowflake, athena, trino, duckdb, druid, clickhouse, databricks, redshift, sqlite, bigquery
@@ -2,13 +2,12 @@ Example to set local credentials for a datasource with key games_dwh:
2
2
 
3
3
  strata ds auth games_dwh
4
4
 
5
- Example to set remote credentials for the same datasource:
6
-
7
- strata ds auth games_dwh -r
8
-
9
- Local credentials are saved in the projects .strata file. This will should not
10
- be checked into git.
11
-
12
- Remote credentials will securely send the credentials to the Strata server. This is
13
- not requires for some modes like OAuth. In that case each user will be prompted to
14
- go through the OAuth flow. Not all adapters support OAuth.
5
+ Credentials are saved in the project's .strata file, which must not be
6
+ checked into git. They are sent to the Strata server with each deploy and
7
+ stored encrypted there.
8
+
9
+ File-based secrets are stored as contents, not paths, so the server never
10
+ needs the file: for BigQuery, give the service account JSON keyfile path and
11
+ only its client_email and private_key are kept; for Snowflake key-pair auth, give the
12
+ private key path and the PEM contents are kept. OAuth modes need no local
13
+ credentials; each user goes through the OAuth flow instead.
@@ -27,8 +27,12 @@ module Strata
27
27
  @ds_key = get_unique_ds_key
28
28
  Output.print_status(:adapter, "adding #{adapter} config to datasources", type: :warning, context: self)
29
29
 
30
- # Interactive mode: write config directly from prompts
31
- config_yaml = {@ds_key => options[:config]}.to_yaml.sub(/^---\n/, "\n")
30
+ # `ds add` passes prompted config; `init -d` renders the commented adapter template.
31
+ config_yaml = if options[:config]
32
+ {@ds_key => options[:config]}.to_yaml.sub(/^---\n/, "\n")
33
+ else
34
+ "\n#{render_template("adapters/#{adapter}.yml")}"
35
+ end
32
36
  append_to_file pathify("datasources.yml"), config_yaml
33
37
  end
34
38
 
@@ -0,0 +1,29 @@
1
+ # Datasource key should be unique in the Project
2
+ <%= @ds_key %>:
3
+ # This is the display name of this datasource
4
+ name: MYDATASOURCENAME
5
+ # Optional description to provide more information in the UI
6
+ description: This DS is the warm tier of our Product Domain
7
+
8
+ # Required. Should be one of hot, warm, or cold. Helps the Query engine
9
+ # prioritize a datasource when multiple can handle the request.
10
+ tier: warm
11
+
12
+ # A valid supported adapter
13
+ adapter: bigquery
14
+
15
+ # default client name sent to the db
16
+ client_name: Strata
17
+
18
+ # Max allowed time a query can run on this Datasource before manual time out
19
+ query_timeout: 3600
20
+
21
+ # Required: GCP project id
22
+ project_id: my-gcp-project
23
+
24
+ # Required: Default dataset for unqualified table names
25
+ dataset: analytics
26
+
27
+ # Authenticate with a service account keyfile by running:
28
+ # `strata ds auth <%= @ds_key %>`
29
+ # keyfile:
@@ -0,0 +1,39 @@
1
+ # Datasource key should be unique in the Project
2
+ <%= @ds_key %>:
3
+ # This is the display name of this datasource
4
+ name: MYDATASOURCENAME
5
+ # Optional description to provide more information in the UI
6
+ description: This DS is the hot tier of our Product Domain
7
+
8
+ # Required. Should be one of hot, warm, or cold. Helps the Query engine
9
+ # prioritize a datasource when multiple can handle the request.
10
+ tier: hot
11
+
12
+ # A valid supported adapter
13
+ adapter: clickhouse
14
+
15
+ # default client name sent to the db
16
+ client_name: Strata
17
+
18
+ # Max allowed time a query can run on this Datasource before manual time out
19
+ query_timeout: 3600
20
+
21
+ # Required: http or https
22
+ protocol: http
23
+
24
+ # Required: ClickHouse server host
25
+ host: localhost
26
+
27
+ # Optional: HTTP port (default: 8123)
28
+ port: 8123
29
+
30
+ # Required: Database name
31
+ database: test_db
32
+
33
+ # Required: Username for connection
34
+ username: default
35
+
36
+ # To set the password securely please run:
37
+ # `strata ds auth <%= @ds_key %>`
38
+ # This command will save your credentials to the .strata global or local file.
39
+ # password:
@@ -0,0 +1,39 @@
1
+ # Datasource key should be unique in the Project
2
+ <%= @ds_key %>:
3
+ # This is the display name of this datasource
4
+ name: MYDATASOURCENAME
5
+ # Optional description to provide more information in the UI
6
+ description: This DS is the warm tier of our Product Domain
7
+
8
+ # Required. Should be one of hot, warm, or cold. Helps the Query engine
9
+ # prioritize a datasource when multiple can handle the request.
10
+ tier: warm
11
+
12
+ # A valid supported adapter
13
+ adapter: redshift
14
+
15
+ # default client name sent to the db
16
+ client_name: Strata
17
+
18
+ # Max allowed time a query can run on this Datasource before manual time out
19
+ query_timeout: 3600
20
+
21
+ # Required: Redshift cluster endpoint
22
+ host: mycluster.abc123.us-east-1.redshift.amazonaws.com
23
+
24
+ # Optional: Port number (default: 5439)
25
+ port: 5439
26
+
27
+ # Required: Database name
28
+ database: dev
29
+
30
+ # Required: Username for connection
31
+ username: awsuser
32
+
33
+ # Optional: Schema name (default: public)
34
+ schema: public
35
+
36
+ # To set the password securely please run:
37
+ # `strata ds auth <%= @ds_key %>`
38
+ # This command will save your credentials to the .strata global or local file.
39
+ # password:
@@ -0,0 +1,22 @@
1
+ # Datasource key should be unique in the Project
2
+ <%= @ds_key %>:
3
+ # This is the display name of this datasource
4
+ name: MYDATASOURCENAME
5
+ # Optional description to provide more information in the UI
6
+ description: This DS is the hot tier of our Product Domain
7
+
8
+ # Required. Should be one of hot, warm, or cold. Helps the Query engine
9
+ # prioritize a datasource when multiple can handle the request.
10
+ tier: hot
11
+
12
+ # A valid supported adapter
13
+ adapter: sqlite
14
+
15
+ # default client name sent to the db
16
+ client_name: Strata
17
+
18
+ # Max allowed time a query can run on this Datasource before manual time out
19
+ query_timeout: 600
20
+
21
+ # Required: Path to the SQLite database file
22
+ file: ./data/warehouse.db
@@ -13,7 +13,8 @@ module Strata
13
13
  athena: %w[aws-sdk-athena aws-sdk-s3 rexml],
14
14
  trino: %w[trino-client],
15
15
  sqlite: %w[sqlite3],
16
- duckdb: %w[duckdb]
16
+ duckdb: %w[duckdb],
17
+ bigquery: %w[google-cloud-bigquery]
17
18
  }.freeze
18
19
 
19
20
  def resolve_datasource(ds_key_arg = nil)
@@ -197,6 +198,8 @@ module Strata
197
198
  "Notes:\n- AWS SDK gems are pure Ruby; no system libraries required."
198
199
  when :trino
199
200
  "Notes:\n- 'trino-client' is pure Ruby; no system libraries required."
201
+ when :bigquery
202
+ "Notes:\n- 'google-cloud-bigquery' is pure Ruby; no system libraries required.\n- Authenticate with a service account keyfile (strata ds auth)."
200
203
  when :duckdb
201
204
  "Notes:\n- See https://github.com/suketa/ruby-duckdb"
202
205
  else
@@ -114,6 +114,12 @@ module Strata
114
114
  if adapter == "databricks"
115
115
  say " Databricks uses OAuth M2M with service principal credentials (client ID + client secret).", :cyan
116
116
  end
117
+ if adapter == "bigquery"
118
+ say " BigQuery: give the service account JSON keyfile path; only its client_email and private_key are stored.", :cyan
119
+ end
120
+ if adapter == "snowflake"
121
+ say " Snowflake key-pair: give the private key path; the PEM contents are stored, not the path.", :cyan
122
+ end
117
123
  say ""
118
124
  creds.collect
119
125
  creds.write_local(ds_key, self)
@@ -140,7 +146,6 @@ module Strata
140
146
 
141
147
  desc "auth DS_KEY", "Set credentials for the given datasource key (DS_KEY)."
142
148
  long_desc_from_file "datasource/auth"
143
- method_option :remote, aliases: ["r"], type: :boolean, desc: "Set credentials on remote server"
144
149
  def auth(ds_key)
145
150
  unless datasources[ds_key]
146
151
  say "Error: Datasource '#{ds_key}' not found in datasources.yml", :red
@@ -161,6 +166,12 @@ module Strata
161
166
  if adapter == "databricks"
162
167
  say " Databricks uses OAuth M2M with service principal credentials (client ID + client secret).", :cyan
163
168
  end
169
+ if adapter == "bigquery"
170
+ say " BigQuery: give the service account JSON keyfile path; only its client_email and private_key are stored.", :cyan
171
+ end
172
+ if adapter == "snowflake"
173
+ say " Snowflake key-pair: give the private key path; the PEM contents are stored, not the path.", :cyan
174
+ end
164
175
  say ""
165
176
  creds.collect
166
177
  creds.write_local(ds_key, self)
@@ -396,6 +407,11 @@ module Strata
396
407
  "database" => "dev",
397
408
  "username" => "awsuser"
398
409
  }
410
+ when "bigquery"
411
+ {
412
+ "project_id" => "my-gcp-project",
413
+ "dataset" => "analytics"
414
+ }
399
415
  else
400
416
  {}
401
417
  end
@@ -404,6 +420,7 @@ module Strata
404
420
  def humanize_field(field)
405
421
  {
406
422
  "account_identifier" => "Account identifier",
423
+ "project_id" => "Project ID",
407
424
  "s3_output_location" => "S3 output location",
408
425
  "auth_mode" => "Auth mode"
409
426
  }.fetch(field, field.split("_").map(&:capitalize).join(" "))
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Strata
4
4
  module CLI
5
- VERSION = "0.1.18"
5
+ VERSION = "0.1.20"
6
6
  end
7
7
  end
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.18
4
+ version: 0.1.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ajo Abraham
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.5.0
19
+ version: 0.6.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.5.0
26
+ version: 0.6.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: aws-sdk-athena
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -224,7 +224,7 @@ description: |
224
224
  Comprehensive CLI tool for managing Strata Semantic Analytics projects. Create and initialize
225
225
  projects, manage datasource connections, build semantic table models with AI assistance, run
226
226
  audits and validations, and deploy projects to Strata servers. Supports multiple data warehouse
227
- adapters including PostgreSQL, MySQL, SQL Server, Snowflake, Athena, Trino, ClickHouse, and more.
227
+ adapters including PostgreSQL, MySQL, SQL Server, Snowflake, BigQuery, Athena, Trino, ClickHouse, and more.
228
228
  email:
229
229
  - ajo@strata.site
230
230
  - allen@strata.site
@@ -274,12 +274,16 @@ files:
274
274
  - lib/strata/cli/generators/table.rb
275
275
  - lib/strata/cli/generators/templates/AGENTS.md
276
276
  - lib/strata/cli/generators/templates/adapters/athena.yml
277
+ - lib/strata/cli/generators/templates/adapters/bigquery.yml
278
+ - lib/strata/cli/generators/templates/adapters/clickhouse.yml
277
279
  - lib/strata/cli/generators/templates/adapters/databricks.yml
278
280
  - lib/strata/cli/generators/templates/adapters/druid.yml
279
281
  - lib/strata/cli/generators/templates/adapters/duckdb.yml
280
282
  - lib/strata/cli/generators/templates/adapters/mysql.yml
281
283
  - lib/strata/cli/generators/templates/adapters/postgres.yml
284
+ - lib/strata/cli/generators/templates/adapters/redshift.yml
282
285
  - lib/strata/cli/generators/templates/adapters/snowflake.yml
286
+ - lib/strata/cli/generators/templates/adapters/sqlite.yml
283
287
  - lib/strata/cli/generators/templates/adapters/sqlserver.yml
284
288
  - lib/strata/cli/generators/templates/adapters/trino.yml
285
289
  - lib/strata/cli/generators/templates/datasources.yml
@@ -344,7 +348,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
344
348
  - !ruby/object:Gem::Version
345
349
  version: '0'
346
350
  requirements: []
347
- rubygems_version: 3.6.7
351
+ rubygems_version: 3.6.9
348
352
  specification_version: 4
349
353
  summary: Command-line interface for the Strata Semantic Analytics System
350
354
  test_files: []