Skip to content

Commit c836590

Browse files
authored
Feat: return non-zero code when at least one audit fails in sqlmesh audit (#4258)
1 parent 65ed579 commit c836590

4 files changed

Lines changed: 48 additions & 4 deletions

File tree

sqlmesh/cli/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,8 @@ def audit(
690690
execution_time: t.Optional[TimeLike] = None,
691691
) -> None:
692692
"""Run audits for the target model(s)."""
693-
obj.audit(models=models, start=start, end=end, execution_time=execution_time)
693+
if not obj.audit(models=models, start=start, end=end, execution_time=execution_time):
694+
exit(1)
694695

695696

696697
@cli.command("check_intervals")

sqlmesh/core/context.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1827,14 +1827,17 @@ def audit(
18271827
*,
18281828
models: t.Optional[t.Iterator[str]] = None,
18291829
execution_time: t.Optional[TimeLike] = None,
1830-
) -> None:
1830+
) -> bool:
18311831
"""Audit models.
18321832
18331833
Args:
18341834
start: The start of the interval to audit.
18351835
end: The end of the interval to audit.
18361836
models: The models to audit. All models will be audited if not specified.
18371837
execution_time: The date/time time reference to use for execution time. Defaults to now.
1838+
1839+
Returns:
1840+
False if any of the audits failed, True otherwise.
18381841
"""
18391842

18401843
snapshots = (
@@ -1845,6 +1848,7 @@ def audit(
18451848

18461849
num_audits = sum(len(snapshot.node.audits_with_args) for snapshot in snapshots)
18471850
self.console.log_status_update(f"Found {num_audits} audit(s).")
1851+
18481852
errors = []
18491853
skipped_count = 0
18501854
for snapshot in snapshots:
@@ -1884,6 +1888,7 @@ def audit(
18841888
)
18851889

18861890
self.console.log_status_update("Done.")
1891+
return not errors
18871892

18881893
@python_api_analytics
18891894
def rewrite(self, sql: str, dialect: str = "") -> exp.Expression:

sqlmesh/magics.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,10 +1000,10 @@ def run_test(self, context: Context, line: str) -> None:
10001000
@argument("--execution-time", type=str, help="Execution time.")
10011001
@line_magic
10021002
@pass_sqlmesh_context
1003-
def audit(self, context: Context, line: str) -> None:
1003+
def audit(self, context: Context, line: str) -> bool:
10041004
"""Run audit(s)"""
10051005
args = parse_argstring(self.audit, line)
1006-
context.audit(
1006+
return context.audit(
10071007
models=args.models, start=args.start, end=args.end, execution_time=args.execution_time
10081008
)
10091009

tests/core/test_context.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2003,3 +2003,41 @@ def test_check_intervals(sushi_context, mocker):
20032003
environment=None, no_signals=False, select_models=["*waiter_as_customer*"], end="next week"
20042004
)
20052005
assert tuple(intervals.values())[0].intervals
2006+
2007+
2008+
def test_audit():
2009+
context = Context(config=Config())
2010+
2011+
parsed_model = parse(
2012+
"""
2013+
MODEL (
2014+
name dummy,
2015+
audits (
2016+
not_null_non_blocking(columns=[c])
2017+
)
2018+
);
2019+
2020+
SELECT NULL AS c
2021+
"""
2022+
)
2023+
context.upsert_model(load_sql_based_model(parsed_model))
2024+
context.plan(no_prompts=True, auto_apply=True)
2025+
2026+
assert context.audit(models=["dummy"], start="2020-01-01", end="2020-01-01") is False
2027+
2028+
parsed_model = parse(
2029+
"""
2030+
MODEL (
2031+
name dummy,
2032+
audits (
2033+
not_null_non_blocking(columns=[c])
2034+
)
2035+
);
2036+
2037+
SELECT 1 AS c
2038+
"""
2039+
)
2040+
context.upsert_model(load_sql_based_model(parsed_model))
2041+
context.plan(no_prompts=True, auto_apply=True)
2042+
2043+
assert context.audit(models=["dummy"], start="2020-01-01", end="2020-01-01") is True

0 commit comments

Comments
 (0)