|
4 | 4 | from typing import Any, NamedTuple, Optional
|
5 | 5 |
|
6 | 6 | import click
|
| 7 | +import yaml |
7 | 8 | from dagster_shared.serdes.objects import LibraryObjectKey
|
8 | 9 | from dagster_shared.yaml_utils import parse_yaml_with_source_positions
|
9 | 10 | from dagster_shared.yaml_utils.source_position import (
|
@@ -62,12 +63,13 @@ class ErrorInput(NamedTuple):
|
62 | 63 | def check_yaml(
|
63 | 64 | dg_context: DgContext,
|
64 | 65 | resolved_paths: Sequence[Path],
|
| 66 | + fix_env_requirements: bool, |
65 | 67 | ) -> bool:
|
66 | 68 | top_level_component_validator = Draft202012Validator(schema=COMPONENT_FILE_SCHEMA)
|
67 | 69 |
|
68 | 70 | validation_errors: list[ErrorInput] = []
|
69 | 71 | all_specified_env_var_deps = set()
|
70 |
| - |
| 72 | + updated_files = set() |
71 | 73 | component_contents_by_key: dict[LibraryObjectKey, Any] = {}
|
72 | 74 | modules_to_fetch = set()
|
73 | 75 | for component_dir in dg_context.defs_path.iterdir():
|
@@ -103,21 +105,38 @@ def check_yaml(
|
103 | 105 | all_specified_env_var_deps.update(specified_env_var_deps)
|
104 | 106 |
|
105 | 107 | if used_env_vars - specified_env_var_deps:
|
106 |
| - msg = ( |
107 |
| - "Component uses environment variables that are not specified in the component file: " |
108 |
| - + ", ".join(used_env_vars - specified_env_var_deps) |
109 |
| - ) |
| 108 | + if fix_env_requirements: |
| 109 | + component_doc_updated = component_doc_tree.value |
| 110 | + if "requires" not in component_doc_updated: |
| 111 | + component_doc_updated["requires"] = {} |
| 112 | + if "env" not in component_doc_updated["requires"]: |
| 113 | + component_doc_updated["requires"]["env"] = [] |
| 114 | + |
| 115 | + component_doc_updated["requires"]["env"].extend( |
| 116 | + used_env_vars - specified_env_var_deps |
| 117 | + ) |
110 | 118 |
|
111 |
| - validation_errors.append( |
112 |
| - ErrorInput( |
113 |
| - None, |
114 |
| - ValidationError( |
115 |
| - msg, |
116 |
| - path=["requires", "env"], |
117 |
| - ), |
118 |
| - component_doc_tree, |
| 119 | + component_path.write_text(yaml.dump(component_doc_updated, sort_keys=False)) |
| 120 | + click.echo(f"Updated {component_path}") |
| 121 | + updated_files.add(component_path) |
| 122 | + all_specified_env_var_deps.update(used_env_vars) |
| 123 | + else: |
| 124 | + msg = ( |
| 125 | + "Component uses environment variables that are not specified in the component file: " |
| 126 | + + ", ".join(used_env_vars - specified_env_var_deps) |
| 127 | + + "\nTo automatically add these environment variables to the component file, run `dg check yaml --fix-env-requirements`" |
| 128 | + ) |
| 129 | + |
| 130 | + validation_errors.append( |
| 131 | + ErrorInput( |
| 132 | + None, |
| 133 | + ValidationError( |
| 134 | + msg, |
| 135 | + path=["requires", "env"], |
| 136 | + ), |
| 137 | + component_doc_tree, |
| 138 | + ) |
119 | 139 | )
|
120 |
| - ) |
121 | 140 |
|
122 | 141 | # First, validate the top-level structure of the component file
|
123 | 142 | # (type and params keys) before we try to validate the params themselves.
|
@@ -175,11 +194,22 @@ def check_yaml(
|
175 | 194 | prefix=["attributes"] if key else [],
|
176 | 195 | )
|
177 | 196 | )
|
| 197 | + if updated_files: |
| 198 | + click.echo( |
| 199 | + "The following component files were updated to fix environment variable requirements:\n" |
| 200 | + + "\n".join(updated_files) |
| 201 | + ) |
178 | 202 | return False
|
179 | 203 | else:
|
180 | 204 | missing_env_vars = (
|
181 | 205 | all_specified_env_var_deps - ProjectEnvVars.from_ctx(dg_context).values.keys()
|
182 | 206 | ) - os.environ.keys()
|
| 207 | + if updated_files: |
| 208 | + click.echo( |
| 209 | + "The following component files were updated to fix environment variable requirements:\n" |
| 210 | + + "\n".join(str(file) for file in updated_files) |
| 211 | + ) |
| 212 | + |
183 | 213 | if missing_env_vars:
|
184 | 214 | click.echo(
|
185 | 215 | "The following environment variables are used in components but not specified in the .env file or the current shell environment:\n"
|
|
0 commit comments