This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/dev/pr-12536-584a25a6e44ea443d8d5e78784dbf75556b2997c in repository https://gitbox.apache.org/repos/asf/seatunnel.git
commit 40d7fef09d203e2b4b1b8a3c5bdfd09ed0b87e58 Author: SEZ <[email protected]> AuthorDate: Thu Oct 1 15:05:40 2026 +0000 [Fix][seatunnel-cli] Treat an exported empty value as a resolved placeholder (#12536) --- seatunnel-cli/benchmark/runner.py | 7 ++- seatunnel-cli/seatunnel_cli/agents.py | 7 ++- seatunnel-cli/tests/test_config_validation.py | 78 +++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/seatunnel-cli/benchmark/runner.py b/seatunnel-cli/benchmark/runner.py index 811602b203..1fa154b5ba 100644 --- a/seatunnel-cli/benchmark/runner.py +++ b/seatunnel-cli/benchmark/runner.py @@ -624,8 +624,11 @@ def main() -> None: # where the user exports them before running seatunnel.sh). from benchmark.execution import CREDENTIALS for key, value in CREDENTIALS.items(): - if value: - os.environ.setdefault(key, value) + # Export empty values too: the benchmark Doris, StarRocks and + # Elasticsearch services genuinely have no password, and skipping + # them left ${DORIS_PASSWORD} looking unresolved, so those tasks + # failed on validation rather than on anything the model produced. + os.environ.setdefault(key, value) results = run_benchmark(models, tasks, levels, args.max_repairs, args.trials, Path(args.out), suite=args.suite) diff --git a/seatunnel-cli/seatunnel_cli/agents.py b/seatunnel-cli/seatunnel_cli/agents.py index 1a5085c7b5..68d519f9cf 100644 --- a/seatunnel-cli/seatunnel_cli/agents.py +++ b/seatunnel-cli/seatunnel_cli/agents.py @@ -553,7 +553,12 @@ def validate_hocon(config_str: str) -> str: var_name = m.group(1) if allowed and allowed.fullmatch(var_name): continue - if not os.environ.get(var_name): + # Test that the variable is set, not that it is non-empty: an + # empty value is a resolved value. Passwordless accounts are + # normal (Doris and StarRocks default to root with no password, + # Elasticsearch to no auth), and `export DORIS_PASSWORD=` must + # not be reported as something still to be exported. + if os.environ.get(var_name) is None: unresolved_vars.add(var_name) if unresolved_vars: var_list = ", ".join(sorted(unresolved_vars)) diff --git a/seatunnel-cli/tests/test_config_validation.py b/seatunnel-cli/tests/test_config_validation.py new file mode 100644 index 0000000000..9067a2a88b --- /dev/null +++ b/seatunnel-cli/tests/test_config_validation.py @@ -0,0 +1,78 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +"""Tests for environment-variable placeholder validation.""" + +import os + +from seatunnel_cli.agents import validate_hocon + + +CONFIG = """ +env { + job.mode = "BATCH" +} +source { + Jdbc { + url = "jdbc:mysql://localhost:3306/shop" + driver = "com.mysql.cj.jdbc.Driver" + user = "root" + password = "${ST_TEST_PASSWORD}" + query = "SELECT * FROM users" + plugin_output = "rows" + } +} +sink { + Console { + plugin_input = "rows" + } +} +""" + +VAR = "ST_TEST_PASSWORD" + + +def _validate_with(value): + """Run validation with VAR set to value, or removed when value is None.""" + previous = os.environ.get(VAR) + had_previous = VAR in os.environ + try: + if value is None: + os.environ.pop(VAR, None) + else: + os.environ[VAR] = value + return validate_hocon(CONFIG) + finally: + if had_previous: + os.environ[VAR] = previous + else: + os.environ.pop(VAR, None) + + +def test_unset_variable_is_reported(): + assert VAR in _validate_with(None) + + +def test_set_variable_is_accepted(): + assert VAR not in _validate_with("Test@123") + + +def test_empty_variable_counts_as_resolved(): + # A passwordless account is normal: Doris and StarRocks default to root + # with no password, Elasticsearch to no auth. `export VAR=` is a resolved + # value, so it must not be reported as still needing an export. + assert VAR not in _validate_with("")
