Skip to content

Commit 62ada5b

Browse files
committed
Add user() config function that reads username from OS
1 parent 0dadda2 commit 62ada5b

4 files changed

Lines changed: 111 additions & 38 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.
@@ -488,15 +535,15 @@ SELECT 2 AS col
488535
└── Directly Modified:
489536
└── sqlmesh_example__dev.test_model
490537

491-
---
492-
+++
493-
494-
495-
kind FULL
496-
)
497-
SELECT
498-
- 1 AS col
499-
+ 2 AS col
538+
---
539+
+++
540+
541+
542+
kind FULL
543+
)
544+
SELECT
545+
- 1 AS col
546+
+ 2 AS col
500547
```
501548

502549
3. Second (metadata) change in `dev`:
@@ -516,27 +563,27 @@ SELECT 5 AS col
516563
└── Directly Modified:
517564
└── sqlmesh_example__dev.test_model
518565

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
566+
---
567+
568+
+++
569+
570+
@@ -1,8 +1,9 @@
571+
572+
MODEL (
573+
name sqlmesh_example.test_model,
574+
+ owner "John Doe",
575+
kind FULL
576+
)
577+
SELECT
578+
- 1 AS col
579+
+ 2 AS col
533580

534581
Directly Modified: sqlmesh_example__dev.test_model (Breaking)
535582
Models needing backfill:
536583
└── sqlmesh_example__dev.test_model: [full refresh]
537584
```
538585
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.
586+
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.
540587
541588
542589
### 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: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,9 +1045,9 @@ def test_loader_for_migrated_dbt_project(tmp_path: Path):
10451045
10461046
model_defaults:
10471047
dialect: bigquery
1048-
1049-
variables:
1050-
__dbt_project_name__: sushi
1048+
1049+
variables:
1050+
__dbt_project_name__: sushi
10511051
""")
10521052

10531053
config = load_config_from_paths(
@@ -1058,20 +1058,46 @@ def test_loader_for_migrated_dbt_project(tmp_path: Path):
10581058
assert config.loader == MigratedDbtProjectLoader
10591059

10601060

1061+
def test_config_user_macro_function(tmp_path: Path) -> None:
1062+
config_path = tmp_path / "config.yaml"
1063+
config_path.write_text("""
1064+
gateways:
1065+
bigquery:
1066+
connection:
1067+
type: bigquery
1068+
project: unit-test
1069+
1070+
default_gateway: bigquery
1071+
1072+
model_defaults:
1073+
dialect: bigquery
1074+
1075+
default_target_environment: dev_{{ user() }}
1076+
""")
1077+
1078+
with mock.patch("getpass.getuser", return_value="test_user"):
1079+
config = load_config_from_paths(
1080+
Config,
1081+
project_paths=[config_path],
1082+
)
1083+
1084+
assert config.default_target_environment == "dev_test_user"
1085+
1086+
10611087
def test_environment_suffix_target_catalog(tmp_path: Path) -> None:
10621088
config_path = tmp_path / "config.yaml"
10631089
config_path.write_text("""
10641090
gateways:
10651091
warehouse:
10661092
connection:
10671093
type: duckdb
1068-
1094+
10691095
default_gateway: warehouse
10701096
10711097
model_defaults:
10721098
dialect: duckdb
1073-
1074-
environment_suffix_target: catalog
1099+
1100+
environment_suffix_target: catalog
10751101
""")
10761102

10771103
config = load_config_from_paths(
@@ -1087,13 +1113,13 @@ def test_environment_suffix_target_catalog(tmp_path: Path) -> None:
10871113
warehouse:
10881114
connection:
10891115
type: duckdb
1090-
1116+
10911117
default_gateway: warehouse
10921118
10931119
model_defaults:
10941120
dialect: duckdb
1095-
1096-
environment_suffix_target: catalog
1121+
1122+
environment_suffix_target: catalog
10971123
10981124
environment_catalog_mapping:
10991125
'.*': "foo"

0 commit comments

Comments
 (0)