Skip to content

Commit 7b4304c

Browse files
committed
Add user() config function that reads username from OS
1 parent 3c9bc36 commit 7b4304c

4 files changed

Lines changed: 105 additions & 32 deletions

File tree

docs/guides/configuration.md

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,53 @@ The examples specify a Snowflake connection whose password is stored in an envir
151151
)
152152
```
153153

154+
#### Default target environment
155+
156+
The SQLMesh `plan` command acts on the `prod` environment by default (i.e., `sqlmesh plan` is equivalent to `sqlmesh plan prod`).
157+
158+
In some organizations, users never run plans directly against `prod` - they do all SQLMesh work in a development environment unique to them. In a standard SQLMesh configuration, this means they need to include their development environment name every time they issue the `plan` command (e.g., `sqlmesh plan dev_tony`).
159+
160+
If your organization works like this, it may be convenient to change the `plan` command's default environment from `prod` to each user's development environment. That way people can issue `sqlmesh plan` without typing the environment name every time.
161+
162+
The SQLMesh configuration `user()` function returns the name of the user currently logged in and running SQLMesh. Call it inside Jinja curly braces to use default target environments based on the user name. The curly braces allow you to combine the user name with a prefix or suffix.
163+
164+
The example configuration below will add the username to the end of the string `dev_`. If the user running SQLMesh is `tony`, the default target environment when they run SQLMesh will be `dev_tony`. In other words, `sqlmesh plan` will be equivalent to `sqlmesh plan dev_tony`.
165+
166+
=== "YAML"
167+
168+
Default target environment is `dev_` combined with the username running SQLMesh.
169+
170+
```yaml
171+
default_target_environment: dev_{{ user() }}
172+
```
173+
174+
=== "Python"
175+
176+
Default target environment is `dev_` combined with the username running SQLMesh.
177+
178+
Retrieve the username with the `getpass.getuser()` function, and combine it with `dev_` in a Python f-string.
179+
180+
```python linenums="1" hl_lines="1 17"
181+
import getpass
182+
import os
183+
from sqlmesh.core.config import (
184+
Config,
185+
ModelDefaultsConfig,
186+
GatewayConfig,
187+
SnowflakeConnectionConfig
188+
)
189+
190+
config = Config(
191+
model_defaults=ModelDefaultsConfig(dialect="duckdb"),
192+
gateways={
193+
"my_gateway": GatewayConfig(
194+
connection=DuckDBConnectionConfig(),
195+
),
196+
},
197+
default_target_environment=f"dev_{getpass.getuser()}",
198+
)
199+
```
200+
154201
### Overrides
155202

156203
Environment variables have the highest precedence among configuration methods, as [noted above](#configuration-files). They will automatically override configuration file specifications if they follow a specific naming structure.
@@ -441,15 +488,15 @@ SELECT 2 AS col
441488
└── Directly Modified:
442489
└── sqlmesh_example__dev.test_model
443490

444-
---
445-
+++
446-
447-
448-
kind FULL
449-
)
450-
SELECT
451-
- 1 AS col
452-
+ 2 AS col
491+
---
492+
+++
493+
494+
495+
kind FULL
496+
)
497+
SELECT
498+
- 1 AS col
499+
+ 2 AS col
453500
```
454501

455502
3. Second (metadata) change in `dev`:
@@ -469,27 +516,27 @@ SELECT 5 AS col
469516
└── Directly Modified:
470517
└── sqlmesh_example__dev.test_model
471518

472-
---
473-
474-
+++
475-
476-
@@ -1,8 +1,9 @@
477-
478-
MODEL (
479-
name sqlmesh_example.test_model,
480-
+ owner "John Doe",
481-
kind FULL
482-
)
483-
SELECT
484-
- 1 AS col
485-
+ 2 AS col
519+
---
520+
521+
+++
522+
523+
@@ -1,8 +1,9 @@
524+
525+
MODEL (
526+
name sqlmesh_example.test_model,
527+
+ owner "John Doe",
528+
kind FULL
529+
)
530+
SELECT
531+
- 1 AS col
532+
+ 2 AS col
486533

487534
Directly Modified: sqlmesh_example__dev.test_model (Breaking)
488535
Models needing backfill:
489536
└── sqlmesh_example__dev.test_model: [full refresh]
490537
```
491538
492-
Even though the second change should have been a metadata change (thus not requiring a backfill), it will still be classified as a breaking change because the comparison is against production instead of the previous development state. This is intentional and may cause additional backfills as more changes are accumulated.
539+
Even though the second change should have been a metadata change (thus not requiring a backfill), it will still be classified as a breaking change because the comparison is against production instead of the previous development state. This is intentional and may cause additional backfills as more changes are accumulated.
493540
494541
495542
### Gateways

sqlmesh/cli/project_init.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,10 @@ def _gen_config(
133133
# --- Optional: Set a default target environment ---
134134
# This is intended for local development to prevent users from accidentally applying plans to the prod environment.
135135
# It is a development only config and should NOT be committed to your git repo.
136-
# https://sqlmesh.readthedocs.io/en/stable/reference/configuration/?h=config#environments
136+
# https://sqlmesh.readthedocs.io/en/stable/guides/configuration/#default-target-environment
137137
138-
# Uncomment one of the following lines (depending on your computer's OS) to use a default target environment derived
139-
# from your system environment variable USER/USERNAME.
140-
# default_target_environment: dev_{{ env_var('USER') }} # Linux/MacOS
141-
# default_target_environment: dev_{{ env_var('USERNAME') }} # Windows
138+
# Uncomment the following line to use a default target environment derived from the logged in user's name.
139+
# default_target_environment: dev_{{ user() }}
142140
143141
# Example usage:
144142
# sqlmesh plan # Automatically resolves to: sqlmesh plan dev_yourname

sqlmesh/utils/yaml.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import getpass
34
import io
45
import typing as t
56
from decimal import Decimal
@@ -14,6 +15,7 @@
1415

1516
JINJA_METHODS = {
1617
"env_var": lambda key, default=None: getenv(key, default),
18+
"user": lambda: getpass.getuser(),
1719
}
1820

1921

tests/core/test_config.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,9 +1044,9 @@ def test_loader_for_migrated_dbt_project(tmp_path: Path):
10441044
10451045
model_defaults:
10461046
dialect: bigquery
1047-
1048-
variables:
1049-
__dbt_project_name__: sushi
1047+
1048+
variables:
1049+
__dbt_project_name__: sushi
10501050
""")
10511051

10521052
config = load_config_from_paths(
@@ -1055,3 +1055,29 @@ def test_loader_for_migrated_dbt_project(tmp_path: Path):
10551055
)
10561056

10571057
assert config.loader == MigratedDbtProjectLoader
1058+
1059+
1060+
def test_config_user_macro_function(tmp_path: Path) -> None:
1061+
config_path = tmp_path / "config.yaml"
1062+
config_path.write_text("""
1063+
gateways:
1064+
bigquery:
1065+
connection:
1066+
type: bigquery
1067+
project: unit-test
1068+
1069+
default_gateway: bigquery
1070+
1071+
model_defaults:
1072+
dialect: bigquery
1073+
1074+
default_target_environment: dev_{{ user() }}
1075+
""")
1076+
1077+
with mock.patch("getpass.getuser", return_value="test_user"):
1078+
config = load_config_from_paths(
1079+
Config,
1080+
project_paths=[config_path],
1081+
)
1082+
1083+
assert config.default_target_environment == "dev_test_user"

0 commit comments

Comments
 (0)