frontier-runner 0.2.2__tar.gz → 0.2.3__tar.gz
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.
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/CHANGELOG.md +8 -0
- {frontier_runner-0.2.2/frontier_runner.egg-info → frontier_runner-0.2.3}/PKG-INFO +35 -9
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/README.md +34 -8
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/__init__.py +1 -1
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/cli.py +217 -55
- frontier_runner-0.2.3/frontier/context.py +653 -0
- frontier_runner-0.2.3/frontier/credentials.py +411 -0
- frontier_runner-0.2.3/frontier/identity.py +137 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/commands.py +123 -23
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/constants.py +1 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/doctor.py +135 -11
- frontier_runner-0.2.3/frontier/onboard/profile_commands.py +408 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/saas.py +7 -3
- frontier_runner-0.2.3/frontier/profiles.py +319 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/warehouse.py +5 -1
- {frontier_runner-0.2.2 → frontier_runner-0.2.3/frontier_runner.egg-info}/PKG-INFO +35 -9
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier_runner.egg-info/SOURCES.txt +4 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/pyproject.toml +1 -1
- frontier_runner-0.2.2/frontier/credentials.py +0 -239
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/LICENSE +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/MANIFEST.in +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/__main__.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/adapters/__init__.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/adapters/base.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/adapters/bigquery.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/adapters/databricks.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/adapters/postgres.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/adapters/redshift.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/adapters/snowflake.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/api.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/artifacts.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/cdc/__init__.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/cdc/config.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/cdc/consume.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/cdc/normalize.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/cdc/prove.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/cdc/route.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/cdc/store.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/cdc/streams.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/cdc/upload.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/certification.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/comment.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/compare.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/config.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/dbt_artifacts.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/environment.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/errors.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/execute.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/filter_v1.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/frontier.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/github.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/hashing.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/impact.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/local_config.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/mutation.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/__init__.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/demo.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/detect.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/discover.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/github.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/gitignore.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/hashkey.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/permissions.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/prompt.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/routes.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/onboard/versions.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/progress.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/proof.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/repair.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/semantic.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/snapshot.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/snowflake.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/snowflake_sql.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/sql_fingerprint.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier/validation.py +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier_runner.egg-info/dependency_links.txt +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier_runner.egg-info/entry_points.txt +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier_runner.egg-info/requires.txt +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/frontier_runner.egg-info/top_level.txt +0 -0
- {frontier_runner-0.2.2 → frontier_runner-0.2.3}/setup.cfg +0 -0
|
@@ -5,6 +5,13 @@ All notable changes to frontier-runner are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.2.3] — 2026-09-27
|
|
11
|
+
|
|
12
|
+
- Named Frontier profiles: local execution contexts that bind a SaaS project credential, API origin, optional dbt target, and optional profiles.yml path without replacing `dbt_project.yml` `profile:`.
|
|
13
|
+
- Named-profile identity uses immutable SaaS organization and project IDs from `whoami`. Names are display-only. Cross-project upload and ingest compare IDs, not names.
|
|
14
|
+
|
|
8
15
|
## [0.2.2] — 2026-09-22
|
|
9
16
|
|
|
10
17
|
- Disposable repair validation is the authoritative repaired-vs-head check. The unchanged materialized mart versus head comparison is an expected pre-repair difference and no longer fails the run.
|
|
@@ -100,6 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
100
107
|
- SaaS commands resolve stored keychain credentials through one shared
|
|
101
108
|
resolver instead of requiring `FRONTIER_API_KEY` in the environment.
|
|
102
109
|
|
|
110
|
+
[0.2.3]: https://github.com/jadsamara/frontier-runner/compare/v0.2.2...v0.2.3
|
|
103
111
|
[0.2.2]: https://github.com/jadsamara/frontier-runner/compare/v0.2.1...v0.2.2
|
|
104
112
|
[0.2.1]: https://github.com/jadsamara/frontier-runner/compare/v0.2.0...v0.2.1
|
|
105
113
|
[0.2.0]: https://github.com/jadsamara/frontier-runner/compare/v0.1.3...v0.2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frontier-runner
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Customer-side Frontier CLI for dbt + Snowflake or BigQuery + GitHub impact assessments.
|
|
5
5
|
Author: Frontier
|
|
6
6
|
License: MIT License
|
|
@@ -92,7 +92,7 @@ Until the package is on PyPI, install the GitHub Release wheel for a version
|
|
|
92
92
|
tag. You do not need a commit SHA:
|
|
93
93
|
|
|
94
94
|
```bash
|
|
95
|
-
pipx install \ "frontier-runner[snowflake] @ https://github.com/jadsamara/frontier-runner/releases/download/v0.2.
|
|
95
|
+
pipx install \ "frontier-runner[snowflake] @ https://github.com/jadsamara/frontier-runner/releases/download/v0.2.3/frontier_runner-0.2.3-py3-none-any.whl"
|
|
96
96
|
pipx inject frontier-runner "snowflake-connector-python>=3.12,<4"
|
|
97
97
|
```
|
|
98
98
|
|
|
@@ -119,6 +119,31 @@ API keys are stored in the OS keychain (or `~/.config/frontier/credentials`
|
|
|
119
119
|
mode 0600). They are never written to `frontier.yml`, `dbt_project.yml`,
|
|
120
120
|
`profiles.yml`, Git, or generated workflows.
|
|
121
121
|
|
|
122
|
+
## Frontier profiles
|
|
123
|
+
|
|
124
|
+
A **Frontier profile** is a local named execution context. It selects one
|
|
125
|
+
Frontier SaaS project credential and may default the dbt target and
|
|
126
|
+
`profiles.yml` path. It does **not** replace the dbt `profile:` in
|
|
127
|
+
`dbt_project.yml`. A **dbt target** is an output such as `dev`, `ci`, or
|
|
128
|
+
`benchmark`.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
frontier profile create repair --target dev --api-key
|
|
132
|
+
frontier profile create benchmark --target benchmark --api-key
|
|
133
|
+
frontier profile use benchmark
|
|
134
|
+
frontier auth status
|
|
135
|
+
frontier doctor
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`--api-key` prompts securely; never pass the raw key as an argument.
|
|
139
|
+
Selection is gitignored in `.frontier/state.yml` and is local to this
|
|
140
|
+
repository. GitHub Actions ignores that file unless `FRONTIER_PROFILE` or
|
|
141
|
+
`--profile` is set; CI continues to use `FRONTIER_API_KEY` /
|
|
142
|
+
`FRONTIER_API_URL` repository secrets. `frontier upload` refuses a run
|
|
143
|
+
generated for another origin, organization, project, dbt project, target, or
|
|
144
|
+
semantic manifest (`ARTIFACT_PROFILE_MISMATCH`). Legacy login without a
|
|
145
|
+
named profile is unchanged.
|
|
146
|
+
|
|
122
147
|
## Developer setup
|
|
123
148
|
|
|
124
149
|
From this repository:
|
|
@@ -200,11 +225,12 @@ assessment.
|
|
|
200
225
|
|
|
201
226
|
`frontier upload` posts `target/frontier-run.json` to `POST /api/v1/runs`. It
|
|
202
227
|
retries HTTP 429/5xx and network errors, and honors `Retry-After`. SaaS
|
|
203
|
-
commands resolve credentials in this order: `FRONTIER_API_KEY`, the
|
|
204
|
-
keychain, the `0600` fallback file, then
|
|
205
|
-
`FRONTIER_ALLOW_LOCAL_MANIFEST` is set
|
|
206
|
-
present, the CLI exits with
|
|
207
|
-
Hashed uploads set
|
|
228
|
+
commands resolve credentials in this order: `FRONTIER_API_KEY`, the selected
|
|
229
|
+
Frontier profile credential, the OS keychain, the `0600` fallback file, then
|
|
230
|
+
`FRONTIER_DEMO_API_KEY` only when `FRONTIER_ALLOW_LOCAL_MANIFEST` is set
|
|
231
|
+
outside GitHub Actions. If none are present, the CLI exits with
|
|
232
|
+
`AUTH_REQUIRED: Run \`frontier login --api-key\``. Hashed uploads set
|
|
233
|
+
`entityIdsHashed: true`.
|
|
208
234
|
|
|
209
235
|
In GitHub Actions, assessments use `{project}-{GITHUB_SHA}` as `externalRunId`
|
|
210
236
|
and record repository, branch, commit, and PR number. After a successful
|
|
@@ -222,9 +248,9 @@ workflow uses `FRONTIER_BLOCKING=false` unless you pass `--blocking`.
|
|
|
222
248
|
Pin an immutable released version:
|
|
223
249
|
|
|
224
250
|
```bash
|
|
225
|
-
pip install "frontier-runner[snowflake]==0.2.
|
|
251
|
+
pip install "frontier-runner[snowflake]==0.2.3"
|
|
226
252
|
# or
|
|
227
|
-
pip install "frontier-runner[bigquery]==0.2.
|
|
253
|
+
pip install "frontier-runner[bigquery]==0.2.3"
|
|
228
254
|
```
|
|
229
255
|
|
|
230
256
|
Until PyPI trusted publishing is reviewed and live, install the GitHub Release
|
|
@@ -34,7 +34,7 @@ Until the package is on PyPI, install the GitHub Release wheel for a version
|
|
|
34
34
|
tag. You do not need a commit SHA:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
pipx install \ "frontier-runner[snowflake] @ https://github.com/jadsamara/frontier-runner/releases/download/v0.2.
|
|
37
|
+
pipx install \ "frontier-runner[snowflake] @ https://github.com/jadsamara/frontier-runner/releases/download/v0.2.3/frontier_runner-0.2.3-py3-none-any.whl"
|
|
38
38
|
pipx inject frontier-runner "snowflake-connector-python>=3.12,<4"
|
|
39
39
|
```
|
|
40
40
|
|
|
@@ -61,6 +61,31 @@ API keys are stored in the OS keychain (or `~/.config/frontier/credentials`
|
|
|
61
61
|
mode 0600). They are never written to `frontier.yml`, `dbt_project.yml`,
|
|
62
62
|
`profiles.yml`, Git, or generated workflows.
|
|
63
63
|
|
|
64
|
+
## Frontier profiles
|
|
65
|
+
|
|
66
|
+
A **Frontier profile** is a local named execution context. It selects one
|
|
67
|
+
Frontier SaaS project credential and may default the dbt target and
|
|
68
|
+
`profiles.yml` path. It does **not** replace the dbt `profile:` in
|
|
69
|
+
`dbt_project.yml`. A **dbt target** is an output such as `dev`, `ci`, or
|
|
70
|
+
`benchmark`.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
frontier profile create repair --target dev --api-key
|
|
74
|
+
frontier profile create benchmark --target benchmark --api-key
|
|
75
|
+
frontier profile use benchmark
|
|
76
|
+
frontier auth status
|
|
77
|
+
frontier doctor
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`--api-key` prompts securely; never pass the raw key as an argument.
|
|
81
|
+
Selection is gitignored in `.frontier/state.yml` and is local to this
|
|
82
|
+
repository. GitHub Actions ignores that file unless `FRONTIER_PROFILE` or
|
|
83
|
+
`--profile` is set; CI continues to use `FRONTIER_API_KEY` /
|
|
84
|
+
`FRONTIER_API_URL` repository secrets. `frontier upload` refuses a run
|
|
85
|
+
generated for another origin, organization, project, dbt project, target, or
|
|
86
|
+
semantic manifest (`ARTIFACT_PROFILE_MISMATCH`). Legacy login without a
|
|
87
|
+
named profile is unchanged.
|
|
88
|
+
|
|
64
89
|
## Developer setup
|
|
65
90
|
|
|
66
91
|
From this repository:
|
|
@@ -142,11 +167,12 @@ assessment.
|
|
|
142
167
|
|
|
143
168
|
`frontier upload` posts `target/frontier-run.json` to `POST /api/v1/runs`. It
|
|
144
169
|
retries HTTP 429/5xx and network errors, and honors `Retry-After`. SaaS
|
|
145
|
-
commands resolve credentials in this order: `FRONTIER_API_KEY`, the
|
|
146
|
-
keychain, the `0600` fallback file, then
|
|
147
|
-
`FRONTIER_ALLOW_LOCAL_MANIFEST` is set
|
|
148
|
-
present, the CLI exits with
|
|
149
|
-
Hashed uploads set
|
|
170
|
+
commands resolve credentials in this order: `FRONTIER_API_KEY`, the selected
|
|
171
|
+
Frontier profile credential, the OS keychain, the `0600` fallback file, then
|
|
172
|
+
`FRONTIER_DEMO_API_KEY` only when `FRONTIER_ALLOW_LOCAL_MANIFEST` is set
|
|
173
|
+
outside GitHub Actions. If none are present, the CLI exits with
|
|
174
|
+
`AUTH_REQUIRED: Run \`frontier login --api-key\``. Hashed uploads set
|
|
175
|
+
`entityIdsHashed: true`.
|
|
150
176
|
|
|
151
177
|
In GitHub Actions, assessments use `{project}-{GITHUB_SHA}` as `externalRunId`
|
|
152
178
|
and record repository, branch, commit, and PR number. After a successful
|
|
@@ -164,9 +190,9 @@ workflow uses `FRONTIER_BLOCKING=false` unless you pass `--blocking`.
|
|
|
164
190
|
Pin an immutable released version:
|
|
165
191
|
|
|
166
192
|
```bash
|
|
167
|
-
pip install "frontier-runner[snowflake]==0.2.
|
|
193
|
+
pip install "frontier-runner[snowflake]==0.2.3"
|
|
168
194
|
# or
|
|
169
|
-
pip install "frontier-runner[bigquery]==0.2.
|
|
195
|
+
pip install "frontier-runner[bigquery]==0.2.3"
|
|
170
196
|
```
|
|
171
197
|
|
|
172
198
|
Until PyPI trusted publishing is reviewed and live, install the GitHub Release
|
|
@@ -15,6 +15,13 @@ from frontier.api import (
|
|
|
15
15
|
redact_api_key,
|
|
16
16
|
upload_run,
|
|
17
17
|
)
|
|
18
|
+
from frontier.context import (
|
|
19
|
+
assert_artifact_matches_context,
|
|
20
|
+
assessment_identity_fields,
|
|
21
|
+
print_execution_banner,
|
|
22
|
+
resolve_execution_context,
|
|
23
|
+
warehouse_connect_kwargs,
|
|
24
|
+
)
|
|
18
25
|
from frontier.credentials import resolve_api_credential, try_resolve_api_credential
|
|
19
26
|
from frontier.comment import maybe_upsert_pr_comment
|
|
20
27
|
from frontier.config import (
|
|
@@ -133,6 +140,14 @@ from frontier.onboard.commands import (
|
|
|
133
140
|
cmd_update_check,
|
|
134
141
|
maybe_version_notice,
|
|
135
142
|
)
|
|
143
|
+
from frontier.onboard.profile_commands import (
|
|
144
|
+
FRONTIER_PROFILE_HELP,
|
|
145
|
+
cmd_profile_create,
|
|
146
|
+
cmd_profile_list,
|
|
147
|
+
cmd_profile_remove,
|
|
148
|
+
cmd_profile_status,
|
|
149
|
+
cmd_profile_use,
|
|
150
|
+
)
|
|
136
151
|
from frontier.onboard.constants import DEFAULT_API_URL
|
|
137
152
|
from frontier.onboard.routes import (
|
|
138
153
|
derive_source_route,
|
|
@@ -344,6 +359,7 @@ def _assessment_identity(
|
|
|
344
359
|
profile_schema: str | None = None,
|
|
345
360
|
pinned_version: int | None = None,
|
|
346
361
|
pinned_fingerprint: str | None = None,
|
|
362
|
+
extra: dict[str, Any] | None = None,
|
|
347
363
|
) -> dict[str, Any]:
|
|
348
364
|
identity: dict[str, Any] = {
|
|
349
365
|
"runnerVersion": __version__,
|
|
@@ -359,6 +375,13 @@ def _assessment_identity(
|
|
|
359
375
|
identity["pinnedManifestVersion"] = pinned_version
|
|
360
376
|
if pinned_fingerprint:
|
|
361
377
|
identity["pinnedManifestFingerprint"] = pinned_fingerprint
|
|
378
|
+
if extra:
|
|
379
|
+
for key, value in extra.items():
|
|
380
|
+
if value in (None, ""):
|
|
381
|
+
continue
|
|
382
|
+
if key in {"credential_ref", "profiles_path"}:
|
|
383
|
+
continue
|
|
384
|
+
identity.setdefault(key, value)
|
|
362
385
|
return identity
|
|
363
386
|
|
|
364
387
|
|
|
@@ -442,6 +465,60 @@ def _config_path(args: argparse.Namespace, project_dir: Path) -> Path:
|
|
|
442
465
|
return project_dir / "frontier.yml"
|
|
443
466
|
|
|
444
467
|
|
|
468
|
+
def _execution_context(args: argparse.Namespace, project_dir: Path):
|
|
469
|
+
return resolve_execution_context(args, project_dir)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _connect_warehouse(args: argparse.Namespace, project_dir: Path, ctx=None):
|
|
473
|
+
current = ctx or _execution_context(args, project_dir)
|
|
474
|
+
return connect_warehouse(project_dir, **warehouse_connect_kwargs(args, current))
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _resolved_dbt_target_label(args: argparse.Namespace, project_dir: Path, config) -> str | None:
|
|
478
|
+
ctx = _execution_context(args, project_dir)
|
|
479
|
+
return getattr(args, "target", None) or ctx.dbt_target or getattr(config, "environment", None)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def _identity_extra(args: argparse.Namespace, project_dir: Path, config) -> dict[str, Any]:
|
|
483
|
+
ctx = _execution_context(args, project_dir)
|
|
484
|
+
if not ctx.selected:
|
|
485
|
+
return {}
|
|
486
|
+
version, fingerprint = _pinned_compare_fields(config)
|
|
487
|
+
return assessment_identity_fields(
|
|
488
|
+
ctx,
|
|
489
|
+
dbt_target=_resolved_dbt_target_label(args, project_dir, config),
|
|
490
|
+
dbt_project=ctx.dbt_project_name or getattr(config, "project", None),
|
|
491
|
+
pinned_version=version,
|
|
492
|
+
pinned_fingerprint=fingerprint,
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def _print_warehouse_context(args: argparse.Namespace, project_dir: Path, ctx, warehouse=None) -> None:
|
|
497
|
+
if not ctx.selected:
|
|
498
|
+
return
|
|
499
|
+
database = schema = warehouse_type = None
|
|
500
|
+
try:
|
|
501
|
+
output = load_dbt_profile_output(
|
|
502
|
+
project_dir,
|
|
503
|
+
**warehouse_connect_kwargs(args, ctx),
|
|
504
|
+
)
|
|
505
|
+
database = str(output.get("database") or output.get("dbname") or output.get("project") or "") or None
|
|
506
|
+
schema = str(output.get("schema") or output.get("dataset") or "") or None
|
|
507
|
+
warehouse_type = str(output.get("type") or "") or None
|
|
508
|
+
except Exception:
|
|
509
|
+
pass
|
|
510
|
+
if warehouse is not None:
|
|
511
|
+
warehouse_type = warehouse.warehouse_type or warehouse_type
|
|
512
|
+
print_execution_banner(
|
|
513
|
+
ctx,
|
|
514
|
+
dbt_profile=ctx.dbt_profile_name,
|
|
515
|
+
dbt_target=ctx.dbt_target or getattr(args, "target", None),
|
|
516
|
+
warehouse_type=warehouse_type,
|
|
517
|
+
database=database,
|
|
518
|
+
schema=schema,
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
|
|
445
522
|
def _api_url(args: argparse.Namespace, config=None, creds=None, local=None) -> str:
|
|
446
523
|
return str(
|
|
447
524
|
getattr(args, "api_url", None)
|
|
@@ -467,9 +544,10 @@ def _resolve_runtime_config(
|
|
|
467
544
|
*,
|
|
468
545
|
dbt_manifest=None,
|
|
469
546
|
):
|
|
470
|
-
|
|
547
|
+
ctx = _execution_context(args, project_dir)
|
|
548
|
+
local = ctx.local
|
|
471
549
|
explicit = getattr(args, "manifest_file", None)
|
|
472
|
-
creds = None if explicit else
|
|
550
|
+
creds = None if explicit else ctx.creds
|
|
473
551
|
api_key = creds.api_key if creds else None
|
|
474
552
|
use_local_yml = allow_local_manifest(args) and not api_key and not explicit
|
|
475
553
|
legacy_path = _config_path(args, project_dir)
|
|
@@ -480,14 +558,21 @@ def _resolve_runtime_config(
|
|
|
480
558
|
# Explicit --config without SaaS credentials still loads the legacy mapping.
|
|
481
559
|
if getattr(args, "config", None):
|
|
482
560
|
legacy = load_frontier_config(legacy_path)
|
|
483
|
-
|
|
561
|
+
if ctx.selected:
|
|
562
|
+
api_url = ctx.api_url
|
|
563
|
+
else:
|
|
564
|
+
api_url = _api_url(args, legacy, creds, local)
|
|
484
565
|
if legacy is not None:
|
|
485
566
|
config = legacy
|
|
486
567
|
else:
|
|
487
568
|
config = saas_runtime_config(
|
|
488
|
-
project=_saas_project_name(project_dir, creds, local),
|
|
569
|
+
project=_saas_project_name(project_dir, creds, local) if not ctx.selected else (ctx.project_name or _saas_project_name(project_dir, creds, local)),
|
|
489
570
|
api_url=api_url,
|
|
490
|
-
environment=
|
|
571
|
+
environment=(
|
|
572
|
+
ctx.dbt_target
|
|
573
|
+
if ctx.selected and ctx.dbt_target
|
|
574
|
+
else (local.dbt_target if local else "dev")
|
|
575
|
+
),
|
|
491
576
|
)
|
|
492
577
|
config, _pinned = resolve_semantic_manifest(
|
|
493
578
|
args,
|
|
@@ -1161,14 +1246,15 @@ def _emit_run(
|
|
|
1161
1246
|
),
|
|
1162
1247
|
**_ingest_manifest_fields(config),
|
|
1163
1248
|
runner_version=__version__,
|
|
1164
|
-
dbt_target=
|
|
1249
|
+
dbt_target=_resolved_dbt_target_label(args, _project_dir(args), config),
|
|
1165
1250
|
assessment_identity=_assessment_identity(
|
|
1166
1251
|
run_id=run_id,
|
|
1167
|
-
dbt_target=
|
|
1252
|
+
dbt_target=_resolved_dbt_target_label(args, _project_dir(args), config),
|
|
1168
1253
|
profile_database=str(model.database) if model.database else None,
|
|
1169
1254
|
profile_schema=str(model.schema) if model.schema else None,
|
|
1170
1255
|
pinned_version=_pinned_compare_fields(config)[0],
|
|
1171
1256
|
pinned_fingerprint=_pinned_compare_fields(config)[1],
|
|
1257
|
+
extra=_identity_extra(args, _project_dir(args), config),
|
|
1172
1258
|
),
|
|
1173
1259
|
)
|
|
1174
1260
|
output = Path(args.output) if args.output else _target_dir(_project_dir(args)) / RUN_FILE_NAME
|
|
@@ -1204,12 +1290,11 @@ def cmd_run(args: argparse.Namespace) -> int:
|
|
|
1204
1290
|
}
|
|
1205
1291
|
)
|
|
1206
1292
|
print("Using in-memory warehouse (--dry-run). No live warehouse session.")
|
|
1293
|
+
_print_warehouse_context(args, project_dir, _execution_context(args, project_dir))
|
|
1207
1294
|
else:
|
|
1208
|
-
warehouse =
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
target=args.target,
|
|
1212
|
-
)
|
|
1295
|
+
warehouse = _connect_warehouse(args, project_dir)
|
|
1296
|
+
ctx = _execution_context(args, project_dir)
|
|
1297
|
+
_print_warehouse_context(args, project_dir, ctx, warehouse)
|
|
1213
1298
|
print(f"{warehouse.warehouse_type}: " + json.dumps(describe_adapter(warehouse)))
|
|
1214
1299
|
|
|
1215
1300
|
base_sql, after_sql = _proof_sql_pair(
|
|
@@ -1535,6 +1620,7 @@ def cmd_prove(args: argparse.Namespace) -> int:
|
|
|
1535
1620
|
}
|
|
1536
1621
|
)
|
|
1537
1622
|
print("Using in-memory warehouse (--dry-run). No live warehouse session.", flush=True)
|
|
1623
|
+
_print_warehouse_context(args, project_dir, _execution_context(args, project_dir))
|
|
1538
1624
|
if sql_change_demo:
|
|
1539
1625
|
sql_proof = recorded_sql_change_proof()
|
|
1540
1626
|
else:
|
|
@@ -1549,17 +1635,12 @@ def cmd_prove(args: argparse.Namespace) -> int:
|
|
|
1549
1635
|
log_step("Snowflake connection started")
|
|
1550
1636
|
live: WarehouseAdapter | None = None
|
|
1551
1637
|
try:
|
|
1552
|
-
live =
|
|
1553
|
-
project_dir,
|
|
1554
|
-
profiles_path=Path(args.profiles).expanduser() if args.profiles else None,
|
|
1555
|
-
target=args.target,
|
|
1556
|
-
)
|
|
1638
|
+
live = _connect_warehouse(args, project_dir)
|
|
1557
1639
|
profile: dict[str, Any] = {}
|
|
1558
1640
|
try:
|
|
1559
1641
|
profile = load_dbt_profile_output(
|
|
1560
1642
|
project_dir,
|
|
1561
|
-
|
|
1562
|
-
target=args.target,
|
|
1643
|
+
**warehouse_connect_kwargs(args, _execution_context(args, project_dir)),
|
|
1563
1644
|
)
|
|
1564
1645
|
except ConfigError:
|
|
1565
1646
|
profile = {}
|
|
@@ -1619,10 +1700,12 @@ def cmd_prove(args: argparse.Namespace) -> int:
|
|
|
1619
1700
|
live.close()
|
|
1620
1701
|
else:
|
|
1621
1702
|
try:
|
|
1622
|
-
warehouse =
|
|
1703
|
+
warehouse = _connect_warehouse(args, project_dir)
|
|
1704
|
+
_print_warehouse_context(
|
|
1705
|
+
args,
|
|
1623
1706
|
project_dir,
|
|
1624
|
-
|
|
1625
|
-
|
|
1707
|
+
_execution_context(args, project_dir),
|
|
1708
|
+
warehouse,
|
|
1626
1709
|
)
|
|
1627
1710
|
except Exception as error:
|
|
1628
1711
|
_write_failed_prove_run(
|
|
@@ -1643,8 +1726,7 @@ def cmd_prove(args: argparse.Namespace) -> int:
|
|
|
1643
1726
|
try:
|
|
1644
1727
|
profile = load_dbt_profile_output(
|
|
1645
1728
|
project_dir,
|
|
1646
|
-
|
|
1647
|
-
target=args.target,
|
|
1729
|
+
**warehouse_connect_kwargs(args, _execution_context(args, project_dir)),
|
|
1648
1730
|
)
|
|
1649
1731
|
except ConfigError:
|
|
1650
1732
|
profile = {}
|
|
@@ -2502,9 +2584,26 @@ def cmd_upload(args: argparse.Namespace) -> int:
|
|
|
2502
2584
|
)
|
|
2503
2585
|
if args.run_id:
|
|
2504
2586
|
payload["externalRunId"] = args.run_id
|
|
2505
|
-
|
|
2587
|
+
ctx = _execution_context(args, project_dir)
|
|
2588
|
+
identity = payload.get("assessmentIdentity") or {}
|
|
2589
|
+
pinned_version = payload.get("semanticManifestVersion")
|
|
2590
|
+
pinned_fingerprint = payload.get("semanticManifestFingerprint")
|
|
2591
|
+
if isinstance(identity, dict):
|
|
2592
|
+
pinned_version = identity.get("pinnedManifestVersion", pinned_version)
|
|
2593
|
+
pinned_fingerprint = identity.get("pinnedManifestFingerprint", pinned_fingerprint)
|
|
2594
|
+
assert_artifact_matches_context(
|
|
2595
|
+
payload,
|
|
2596
|
+
ctx,
|
|
2597
|
+
dbt_project=ctx.dbt_project_name,
|
|
2598
|
+
dbt_target=ctx.dbt_target or getattr(args, "target", None),
|
|
2599
|
+
pinned_version=pinned_version if isinstance(pinned_version, int) else None,
|
|
2600
|
+
pinned_fingerprint=str(pinned_fingerprint) if pinned_fingerprint else None,
|
|
2601
|
+
)
|
|
2602
|
+
creds = ctx.creds
|
|
2603
|
+
if creds is None:
|
|
2604
|
+
creds = resolve_api_credential()
|
|
2506
2605
|
api_key, api_key_source = creds.api_key, creds.source
|
|
2507
|
-
api_url = _api_url(args, None, creds, local)
|
|
2606
|
+
api_url = ctx.api_url if ctx.selected else _api_url(args, None, creds, local)
|
|
2508
2607
|
print(
|
|
2509
2608
|
f"Uploading {payload.get('externalRunId')} to {api_url} "
|
|
2510
2609
|
f"as {redact_api_key(api_key)} ({api_key_source})",
|
|
@@ -2541,11 +2640,11 @@ def cmd_upload(args: argparse.Namespace) -> int:
|
|
|
2541
2640
|
|
|
2542
2641
|
def cmd_manifest_fetch(args: argparse.Namespace) -> int:
|
|
2543
2642
|
project_dir = _project_dir(args)
|
|
2544
|
-
|
|
2545
|
-
creds = resolve_api_credential()
|
|
2643
|
+
ctx = _execution_context(args, project_dir)
|
|
2644
|
+
creds = ctx.creds or resolve_api_credential()
|
|
2546
2645
|
api_key = creds.api_key
|
|
2547
|
-
api_url = _api_url(args, None, creds, local)
|
|
2548
|
-
project = _saas_project_name(project_dir, creds, local)
|
|
2646
|
+
api_url = ctx.api_url if ctx.selected else _api_url(args, None, creds, ctx.local)
|
|
2647
|
+
project = ctx.project_name or _saas_project_name(project_dir, creds, ctx.local)
|
|
2549
2648
|
output = Path(args.output).expanduser().resolve() if args.output else default_pin_path(project_dir)
|
|
2550
2649
|
started = time.perf_counter()
|
|
2551
2650
|
log_step("manifest fetch started", prefix="manifest")
|
|
@@ -2582,6 +2681,17 @@ def _add_project_dir(parser: argparse.ArgumentParser) -> None:
|
|
|
2582
2681
|
dest="project_dir_opt",
|
|
2583
2682
|
help="dbt project directory (same as the positional path)",
|
|
2584
2683
|
)
|
|
2684
|
+
_add_profile_option(parser)
|
|
2685
|
+
|
|
2686
|
+
|
|
2687
|
+
def _add_profile_option(parser: argparse.ArgumentParser) -> None:
|
|
2688
|
+
parser.add_argument(
|
|
2689
|
+
"--profile",
|
|
2690
|
+
dest="frontier_profile",
|
|
2691
|
+
metavar="NAME",
|
|
2692
|
+
default=None,
|
|
2693
|
+
help=FRONTIER_PROFILE_HELP,
|
|
2694
|
+
)
|
|
2585
2695
|
|
|
2586
2696
|
|
|
2587
2697
|
def _add_manifest_flags(parser: argparse.ArgumentParser) -> None:
|
|
@@ -2641,6 +2751,7 @@ def cmd_cdc_inspect(args: argparse.Namespace) -> int:
|
|
|
2641
2751
|
_config_path(args, project_dir).is_file()
|
|
2642
2752
|
or getattr(args, "manifest_file", None)
|
|
2643
2753
|
or try_resolve_api_credential() is not None
|
|
2754
|
+
or _execution_context(args, project_dir).creds is not None
|
|
2644
2755
|
):
|
|
2645
2756
|
dbt_path = _target_dir(project_dir) / "manifest.json"
|
|
2646
2757
|
dbt_manifest = load_manifest(dbt_path) if dbt_path.is_file() else None
|
|
@@ -2664,11 +2775,8 @@ def cmd_cdc_inspect(args: argparse.Namespace) -> int:
|
|
|
2664
2775
|
def cmd_cdc_status(args: argparse.Namespace) -> int:
|
|
2665
2776
|
project_dir = _project_dir(args)
|
|
2666
2777
|
config = load_cdc_config(_cdc_path(args, project_dir))
|
|
2667
|
-
warehouse =
|
|
2668
|
-
|
|
2669
|
-
profiles_path=Path(args.profiles).expanduser() if getattr(args, "profiles", None) else None,
|
|
2670
|
-
target=getattr(args, "target", None),
|
|
2671
|
-
)
|
|
2778
|
+
warehouse = _connect_warehouse(args, project_dir)
|
|
2779
|
+
_print_warehouse_context(args, project_dir, _execution_context(args, project_dir), warehouse)
|
|
2672
2780
|
_assert_cdc_supported(warehouse)
|
|
2673
2781
|
try:
|
|
2674
2782
|
store = SnowflakeCdcStore(warehouse, config)
|
|
@@ -2686,11 +2794,8 @@ def cmd_cdc_status(args: argparse.Namespace) -> int:
|
|
|
2686
2794
|
def cmd_cdc_consume(args: argparse.Namespace) -> int:
|
|
2687
2795
|
project_dir = _project_dir(args)
|
|
2688
2796
|
config = load_cdc_config(_cdc_path(args, project_dir))
|
|
2689
|
-
warehouse =
|
|
2690
|
-
|
|
2691
|
-
profiles_path=Path(args.profiles).expanduser() if getattr(args, "profiles", None) else None,
|
|
2692
|
-
target=getattr(args, "target", None),
|
|
2693
|
-
)
|
|
2797
|
+
warehouse = _connect_warehouse(args, project_dir)
|
|
2798
|
+
_print_warehouse_context(args, project_dir, _execution_context(args, project_dir), warehouse)
|
|
2694
2799
|
_assert_cdc_supported(warehouse)
|
|
2695
2800
|
started = time.perf_counter()
|
|
2696
2801
|
log_step("consume started", prefix="cdc")
|
|
@@ -2732,11 +2837,8 @@ def cmd_cdc_prove(args: argparse.Namespace) -> int:
|
|
|
2732
2837
|
cdc_config = load_cdc_config(_cdc_path(args, project_dir))
|
|
2733
2838
|
frontier_config = _resolve_runtime_config(args, project_dir, dbt_manifest=manifest)
|
|
2734
2839
|
cdc_config = overlay_cdc_with_manifest(cdc_config, frontier_config.pinned)
|
|
2735
|
-
warehouse =
|
|
2736
|
-
|
|
2737
|
-
profiles_path=Path(args.profiles).expanduser() if getattr(args, "profiles", None) else None,
|
|
2738
|
-
target=getattr(args, "target", None),
|
|
2739
|
-
)
|
|
2840
|
+
warehouse = _connect_warehouse(args, project_dir)
|
|
2841
|
+
_print_warehouse_context(args, project_dir, _execution_context(args, project_dir), warehouse)
|
|
2740
2842
|
_assert_cdc_supported(warehouse)
|
|
2741
2843
|
started = time.perf_counter()
|
|
2742
2844
|
log_step("prove started", prefix="cdc")
|
|
@@ -2799,15 +2901,13 @@ def cmd_cdc_upload(args: argparse.Namespace) -> int:
|
|
|
2799
2901
|
cdc_config = load_cdc_config(_cdc_path(args, project_dir))
|
|
2800
2902
|
frontier_config = _resolve_runtime_config(args, project_dir, dbt_manifest=manifest)
|
|
2801
2903
|
cdc_config = overlay_cdc_with_manifest(cdc_config, frontier_config.pinned)
|
|
2802
|
-
warehouse =
|
|
2803
|
-
|
|
2804
|
-
profiles_path=Path(args.profiles).expanduser() if getattr(args, "profiles", None) else None,
|
|
2805
|
-
target=getattr(args, "target", None),
|
|
2806
|
-
)
|
|
2904
|
+
warehouse = _connect_warehouse(args, project_dir)
|
|
2905
|
+
_print_warehouse_context(args, project_dir, _execution_context(args, project_dir), warehouse)
|
|
2807
2906
|
_assert_cdc_supported(warehouse)
|
|
2808
|
-
|
|
2907
|
+
ctx = _execution_context(args, project_dir)
|
|
2908
|
+
creds = ctx.creds or resolve_api_credential()
|
|
2809
2909
|
api_key, api_key_source = creds.api_key, creds.source
|
|
2810
|
-
api_url = _api_url(args, frontier_config, creds)
|
|
2910
|
+
api_url = ctx.api_url if ctx.selected else _api_url(args, frontier_config, creds)
|
|
2811
2911
|
print(
|
|
2812
2912
|
f"Uploading CDC assessment to {api_url} "
|
|
2813
2913
|
f"as {redact_api_key(api_key)} ({api_key_source})",
|
|
@@ -2852,9 +2952,15 @@ def cmd_cdc_upload(args: argparse.Namespace) -> int:
|
|
|
2852
2952
|
def build_parser() -> argparse.ArgumentParser:
|
|
2853
2953
|
parser = argparse.ArgumentParser(
|
|
2854
2954
|
prog="frontier",
|
|
2855
|
-
description=
|
|
2955
|
+
description=(
|
|
2956
|
+
"Run a change-frontier assessment in the customer environment. "
|
|
2957
|
+
"A Frontier profile is a local named execution context. It selects a Frontier "
|
|
2958
|
+
"SaaS project credential and may provide a default dbt target and profiles.yml "
|
|
2959
|
+
"path. It does not replace the dbt `profile:` declared by dbt_project.yml."
|
|
2960
|
+
),
|
|
2856
2961
|
)
|
|
2857
2962
|
parser.add_argument("--version", action="version", version=f"frontier {__version__}")
|
|
2963
|
+
_add_profile_option(parser)
|
|
2858
2964
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
2859
2965
|
|
|
2860
2966
|
init = sub.add_parser("init", help="Detect the dbt project and write .frontier/config.yml")
|
|
@@ -2874,18 +2980,74 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2874
2980
|
signup.set_defaults(func=cmd_signup)
|
|
2875
2981
|
|
|
2876
2982
|
login = sub.add_parser("login", help="Store a project API key in the OS keychain")
|
|
2877
|
-
login.add_argument("--api-key", action="store_true", help="Paste a project API key (hidden input)")
|
|
2983
|
+
login.add_argument("--api-key", action="store_true", help="Paste a project API key (hidden input). Never pass the key as a positional argument.")
|
|
2878
2984
|
login.add_argument("--api-url", help="SaaS origin")
|
|
2985
|
+
_add_profile_option(login)
|
|
2879
2986
|
login.set_defaults(func=cmd_login)
|
|
2880
2987
|
|
|
2881
2988
|
logout = sub.add_parser("logout", help="Remove stored Frontier API credentials")
|
|
2989
|
+
_add_profile_option(logout)
|
|
2990
|
+
logout.add_argument(
|
|
2991
|
+
"--remove-metadata",
|
|
2992
|
+
action="store_true",
|
|
2993
|
+
help="Also delete the named Frontier profile metadata (profile logout only)",
|
|
2994
|
+
)
|
|
2882
2995
|
logout.set_defaults(func=cmd_logout)
|
|
2883
2996
|
|
|
2884
2997
|
auth = sub.add_parser("auth", help="Show authentication status")
|
|
2885
2998
|
auth_sub = auth.add_subparsers(dest="auth_command", required=True)
|
|
2886
2999
|
auth_status = auth_sub.add_parser("status", help="Show whether the CLI is authenticated")
|
|
3000
|
+
_add_profile_option(auth_status)
|
|
2887
3001
|
auth_status.set_defaults(func=cmd_auth_status)
|
|
2888
3002
|
|
|
3003
|
+
profile = sub.add_parser(
|
|
3004
|
+
"profile",
|
|
3005
|
+
help=(
|
|
3006
|
+
"Create and select named Frontier profiles. A Frontier profile is a local "
|
|
3007
|
+
"execution context for a SaaS project credential; it does not replace the "
|
|
3008
|
+
"dbt `profile:` in dbt_project.yml."
|
|
3009
|
+
),
|
|
3010
|
+
)
|
|
3011
|
+
profile_sub = profile.add_subparsers(dest="profile_command", required=True)
|
|
3012
|
+
profile_create = profile_sub.add_parser(
|
|
3013
|
+
"create",
|
|
3014
|
+
help="Verify a project API key and store a named Frontier profile",
|
|
3015
|
+
)
|
|
3016
|
+
profile_create.add_argument("name", help="Frontier profile name")
|
|
3017
|
+
profile_create.add_argument("--api-url", help="SaaS origin")
|
|
3018
|
+
profile_create.add_argument("--target", help="Default dbt target for this Frontier profile")
|
|
3019
|
+
profile_create.add_argument("--profiles", help="Optional profiles.yml path for this Frontier profile")
|
|
3020
|
+
profile_create.add_argument(
|
|
3021
|
+
"--api-key",
|
|
3022
|
+
action="store_true",
|
|
3023
|
+
help="Prompt securely for the project API key (never pass the key as an argument)",
|
|
3024
|
+
)
|
|
3025
|
+
profile_create.add_argument("--force", action="store_true", help="Replace an existing Frontier profile")
|
|
3026
|
+
profile_create.add_argument("--yes", action="store_true", help="Accept the default API origin")
|
|
3027
|
+
_add_project_dir(profile_create)
|
|
3028
|
+
profile_create.set_defaults(func=cmd_profile_create)
|
|
3029
|
+
profile_list = profile_sub.add_parser("list", help="List local Frontier profiles")
|
|
3030
|
+
_add_project_dir(profile_list)
|
|
3031
|
+
profile_list.set_defaults(func=cmd_profile_list)
|
|
3032
|
+
profile_use = profile_sub.add_parser("use", help="Select a Frontier profile for this dbt project")
|
|
3033
|
+
profile_use.add_argument("name", help="Frontier profile name")
|
|
3034
|
+
profile_use.add_argument(
|
|
3035
|
+
"--offline",
|
|
3036
|
+
action="store_true",
|
|
3037
|
+
help="Skip SaaS revalidation when the credential is present",
|
|
3038
|
+
)
|
|
3039
|
+
_add_project_dir(profile_use)
|
|
3040
|
+
profile_use.set_defaults(func=cmd_profile_use)
|
|
3041
|
+
profile_status = profile_sub.add_parser("status", help="Show the resolved Frontier execution context")
|
|
3042
|
+
_add_project_dir(profile_status)
|
|
3043
|
+
profile_status.set_defaults(func=cmd_profile_status)
|
|
3044
|
+
profile_remove = profile_sub.add_parser("remove", help="Remove a named Frontier profile")
|
|
3045
|
+
profile_remove.add_argument("name", help="Frontier profile name")
|
|
3046
|
+
profile_remove.add_argument("--force", action="store_true", help="Remove even if this profile is selected")
|
|
3047
|
+
profile_remove.add_argument("--yes", action="store_true", help="Delete the stored credential without prompting")
|
|
3048
|
+
_add_project_dir(profile_remove)
|
|
3049
|
+
profile_remove.set_defaults(func=cmd_profile_remove)
|
|
3050
|
+
|
|
2889
3051
|
discover = sub.add_parser(
|
|
2890
3052
|
"discover",
|
|
2891
3053
|
help="Derive and upload a generated semantic configuration from dbt artifacts",
|
|
@@ -3113,7 +3275,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
3113
3275
|
parser = build_parser()
|
|
3114
3276
|
args = parser.parse_args(argv)
|
|
3115
3277
|
command = getattr(args, "command", None)
|
|
3116
|
-
if command not in {"update-check", "doctor", "login", "logout", "signup"}:
|
|
3278
|
+
if command not in {"update-check", "doctor", "login", "logout", "signup", "profile"}:
|
|
3117
3279
|
from frontier.onboard.constants import DEFAULT_API_URL
|
|
3118
3280
|
|
|
3119
3281
|
maybe_version_notice(str(getattr(args, "api_url", None) or DEFAULT_API_URL))
|