Skip to content

Commit 59dc772

Browse files
authored
fix: decode feature.json as UTF-8 in Windows PowerShell (#4359)
* fix: read feature.json as UTF-8 in PowerShell Assisted-by: Codex (model: GPT-5, autonomous) * test: write Unicode feature path as UTF-8 Assisted-by: Codex (model: GPT-5, autonomous) --------- Co-authored-by: Hamed Rabah <26891088+hamedrabah@users.noreply.github.com>
1 parent 2dc229b commit 59dc772

2 files changed

Lines changed: 37 additions & 3 deletions

File tree

scripts/powershell/common.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ function Save-FeatureJson {
135135
# Read current value and skip write when unchanged
136136
if (Test-Path -LiteralPath $fjPath -PathType Leaf) {
137137
try {
138-
$raw = Get-Content -LiteralPath $fjPath -Raw
138+
$raw = [System.IO.File]::ReadAllText($fjPath, [System.Text.Encoding]::UTF8)
139139
$cfg = $raw | ConvertFrom-Json
140140
if ($cfg.feature_directory -eq $FeatureDirectory) {
141141
return
@@ -187,7 +187,7 @@ function Get-FeaturePathsEnv {
187187
Save-FeatureJson -RepoRoot $repoRoot -FeatureDirectory $env:SPECIFY_FEATURE_DIRECTORY
188188
}
189189
} elseif (Test-Path $featureJson) {
190-
$featureJsonRaw = Get-Content -LiteralPath $featureJson -Raw
190+
$featureJsonRaw = [System.IO.File]::ReadAllText($featureJson, [System.Text.Encoding]::UTF8)
191191
try {
192192
$featureConfig = $featureJsonRaw | ConvertFrom-Json
193193
} catch {

tests/test_check_prerequisites_paths_only.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def _write_feature_json(
3838
repo: Path, feature_directory: str = "specs/001-my-feature"
3939
) -> None:
4040
(repo / ".specify" / "feature.json").write_text(
41-
json.dumps({"feature_directory": feature_directory}),
41+
json.dumps({"feature_directory": feature_directory}, ensure_ascii=False),
4242
encoding="utf-8",
4343
)
4444

@@ -288,6 +288,40 @@ def test_ps_paths_only_succeeds_on_non_spec_branch(prereq_repo: Path) -> None:
288288
assert "FEATURE_DIR" in data
289289

290290

291+
@pytest.mark.skipif(
292+
not _WINDOWS_POWERSHELL, reason="Windows PowerShell 5.1 not available"
293+
)
294+
def test_windows_powershell_reads_bomless_utf8_feature_json(
295+
prereq_repo: Path,
296+
) -> None:
297+
"""Windows PowerShell must decode non-ASCII feature paths as UTF-8 (#4333)."""
298+
feature_directory = "specs/001-后台信息架构"
299+
feature_path = prereq_repo / feature_directory
300+
feature_path.mkdir(parents=True)
301+
_write_feature_json(prereq_repo, feature_directory)
302+
303+
resolved_path = prereq_repo / "resolved-feature-path.txt"
304+
common_ps = prereq_repo / ".specify" / "scripts" / "powershell" / "common.ps1"
305+
ps_command = (
306+
f". '{common_ps}'; "
307+
"$resolved = Get-FeaturePathsEnv -NoPersist; "
308+
"$utf8NoBom = New-Object System.Text.UTF8Encoding($false); "
309+
f"[System.IO.File]::WriteAllText('{resolved_path}', "
310+
"[string]$resolved.FEATURE_DIR, $utf8NoBom)"
311+
)
312+
result = subprocess.run(
313+
[_WINDOWS_POWERSHELL, "-NoProfile", "-Command", ps_command],
314+
cwd=prereq_repo,
315+
capture_output=True,
316+
text=True,
317+
check=False,
318+
env=_clean_env(),
319+
)
320+
321+
assert result.returncode == 0, result.stderr
322+
assert resolved_path.read_text(encoding="utf-8") == str(feature_path)
323+
324+
291325
@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available")
292326
@pytest.mark.parametrize(
293327
("use_env_var", "specify_feature", "expected_branch"),

0 commit comments

Comments
 (0)