You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
key_value_replace skipped Semantic Model .tmdl files because only JSON/YAML content was recognized. This PR adds targeted TMDL support for name-based replacements in Semantic Model parameter and data source definitions, without introducing new parameter types or external dependencies.
TMDL detection + routing
Added .tmdl file detection in _check_utils.py (check_tmdl_file).
Extended FabricWorkspace._replace_parameters to invoke a TMDL-specific replacement path after JSON/YAML checks.
TMDL key-value replacement engine
Added replace_key_value_tmdl(...) in _parameter/_utils.py.
Supports:
expression.<ParameterName> → replaces M-query parameter value while preserving meta [...].
dataSource.<SourceName>.<nested>.<property> → replaces targeted scalar key: value nodes in nested blocks.
Semantic Model items in Fabric Git integration use TMDL (Tabular Model Definition Language) — a custom indentation-based DSL — not JSON or YAML. This means key_value_replace in parameter.yml currently silently skips all Semantic Model .tmdl files because check_valid_json_content and check_valid_yaml_content both return False for TMDL content.
This blocks users from doing name-based parameter replacement in Semantic Models (see issue #552), which is the correct, regex-free, schema-aware approach requested by users.
Goal
Extend key_value_replace to support .tmdl files by adding a minimal, purpose-built TMDL key-value handler — no external dependency, no full TMDL parser needed. The scope is limited to the two constructs users need to parameterize:
M-query parameters in expressions.tmdl — the expression <Name> = block
Data source connection details in dataSources/<Name>.tmdl — nested key: value scalar properties
TMDL File Format Reference
TMDL is tab-indented. The two relevant constructs look like this:
Scalar properties are key: value lines at various indent levels within a named block.
Implementation Plan
1. src/fabric_cicd/_common/_check_utils.py
Add a function to detect TMDL files by extension (content is not self-describing like JSON/YAML):
defcheck_tmdl_file(file_path: Path) ->bool:
"""Check if the file is a TMDL file by extension."""returnstr(file_path).endswith(".tmdl")
2. src/fabric_cicd/_parameter/_utils.py
Add a new replace_key_value_tmdl function that:
Accepts the same signature pattern as replace_key_value: (workspace_obj, param_dict, content, env)
Reads find_key as a dot-separated path, where:
The first segment is the TMDL block type (expression or dataSource)
The second segment is the block name (e.g. DatabaseServer, ExtSql)
Any further segments navigate nested properties (e.g. connectionDetails.address.server)
For expression blocks with no further segments, replace the value portion of the expression <Name> = value line (preserving any meta [...] suffix)
Parses the file line by line, tracking indentation context to locate the correct named block and then the correct key within it
Replaces only the target value in-place — all other lines, whitespace, comments, and structure must be preserved exactly (TMDL is whitespace-significant)
Returns the modified content as a string
Calls extract_replace_value for dynamic variable support ($workspace, $items) consistent with the rest of the parameterization system
Logs a debug message if the find_key matches 0 nodes (similar to existing behaviour)
find_key format examples:
expression.DatabaseServer — replace the value of the M-query parameter named DatabaseServer
dataSource.ExtSql.connectionDetails.address.server — replace the server property within the address block of the ExtSql data source
CopilotAI
changed the title
[WIP] Extend key_value_replace to support TMDL files
Fixes #552 - Add TMDL-aware key_value_replace for Semantic Models
May 31, 2026
I addressed the unit test failures by importing patch, patching the correct logger target in the TMDL no-match test, and updating the dynamic workspace-id assertion to match the fixture value. I also re-ran tests locally: tests/test_parameter_utils.py passes and the full test suite passes (905 passed, 6 skipped).
I fixed the lint failure by organizing the import blocks in src/fabric_cicd/fabric_workspace.py and tests/test__check_utils.py, then validated with ruff check, ruff format --check, and targeted tests (tests/test__check_utils.py and tests/test_parameter_utils.py).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
key_value_replaceskipped Semantic Model.tmdlfiles because only JSON/YAML content was recognized. This PR adds targeted TMDL support for name-based replacements in Semantic Model parameter and data source definitions, without introducing new parameter types or external dependencies.TMDL detection + routing
.tmdlfile detection in_check_utils.py(check_tmdl_file).FabricWorkspace._replace_parametersto invoke a TMDL-specific replacement path after JSON/YAML checks.TMDL key-value replacement engine
replace_key_value_tmdl(...)in_parameter/_utils.py.expression.<ParameterName>→ replaces M-query parameter value while preservingmeta [...].dataSource.<SourceName>.<nested>.<property>→ replaces targeted scalarkey: valuenodes in nested blocks.process_environment_key,extract_replace_value).find_keyresolves to 0 targets.Tests
check_tmdl_filecoverage intests/test__check_utils.py.replace_key_value_tmdlcoverage intests/test_parameter_utils.py:$workspace)Docs + sample artifacts
docs/how_to/parameterization.mdwith a Semantic Models section and canonicalfind_keyexamples.definition/expressions.tmdldefinition/dataSources/ExtSql.tmdlLinked Issue (REQUIRED)
Linked via PR title.
Original prompt
Background
Semantic Model items in Fabric Git integration use TMDL (Tabular Model Definition Language) — a custom indentation-based DSL — not JSON or YAML. This means
key_value_replaceinparameter.ymlcurrently silently skips all Semantic Model.tmdlfiles becausecheck_valid_json_contentandcheck_valid_yaml_contentboth returnFalsefor TMDL content.This blocks users from doing name-based parameter replacement in Semantic Models (see issue #552), which is the correct, regex-free, schema-aware approach requested by users.
Goal
Extend
key_value_replaceto support.tmdlfiles by adding a minimal, purpose-built TMDL key-value handler — no external dependency, no full TMDL parser needed. The scope is limited to the two constructs users need to parameterize:expressions.tmdl— theexpression <Name> =blockdataSources/<Name>.tmdl— nestedkey: valuescalar propertiesTMDL File Format Reference
TMDL is tab-indented. The two relevant constructs look like this:
expressions.tmdl— M-query parameter:The value is the first indented line after
expression <Name> =, up to and including anymeta [...]suffix.dataSources/ExtSql.tmdl— connection detail:Scalar properties are
key: valuelines at various indent levels within a named block.Implementation Plan
1.
src/fabric_cicd/_common/_check_utils.pyAdd a function to detect TMDL files by extension (content is not self-describing like JSON/YAML):
2.
src/fabric_cicd/_parameter/_utils.pyAdd a new
replace_key_value_tmdlfunction that:replace_key_value:(workspace_obj, param_dict, content, env)find_keyas a dot-separated path, where:expressionordataSource)DatabaseServer,ExtSql)connectionDetails.address.server)expressionblocks with no further segments, replace the value portion of theexpression <Name> =value line (preserving anymeta [...]suffix)extract_replace_valuefor dynamic variable support ($workspace,$items) consistent with the rest of the parameterization systemfind_keymatches 0 nodes (similar to existing behaviour)find_keyformat examples:expression.DatabaseServer— replace the value of the M-query parameter namedDatabaseServerdataSource.ExtSql.connectionDetails.address.server— replace theserverproperty within theaddressblock of theExtSqldata source3.
src/fabric_cicd/fabric_workspace.py—_replace_parametersAdd a third branch after the existing JSON and YAML checks:
4.
tests/test_parameter_utils.pyAdd tests for
replace_key_value_tmdlcovering:expressionblock, preservingmeta [...]suffixdataSourceproperty by name path$workspace.$id,$items.*) works correctlyreplace_value— content returned unchanged5.
tests/test__check_utils.pyAdd tests for
check_tmdl_file.6.
docs/how_to/parameterization.mdAdd a
### Semantic Modelssection under## Examples by Item Typedocumenting:This pull request was created from Copilot chat.