diff --git a/scripts/bash/check-prerequisites.sh b/scripts/bash/check-prerequisites.sh index 2c1b8e1351..5d2eb08183 100644 --- a/scripts/bash/check-prerequisites.sh +++ b/scripts/bash/check-prerequisites.sh @@ -79,7 +79,11 @@ SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/common.sh" # Get feature paths -_paths_output=$(get_feature_paths) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; } +if $PATHS_ONLY; then + _paths_output=$(SPECIFY_NO_PERSIST_FEATURE_JSON=1 get_feature_paths) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; } +else + _paths_output=$(get_feature_paths) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; } +fi eval "$_paths_output" unset _paths_output diff --git a/scripts/bash/common.sh b/scripts/bash/common.sh index 3ea66a652d..cf565877d4 100644 --- a/scripts/bash/common.sh +++ b/scripts/bash/common.sh @@ -95,6 +95,10 @@ _persist_feature_json() { local feature_dir_value="$2" local fj="$repo_root/.specify/feature.json" + if [[ "${SPECIFY_NO_PERSIST_FEATURE_JSON:-}" == "1" ]]; then + return 0 + fi + # Strip repo_root prefix if the value is absolute and under repo_root if [[ "$feature_dir_value" == "$repo_root/"* ]]; then feature_dir_value="${feature_dir_value#"$repo_root/"}" diff --git a/scripts/powershell/check-prerequisites.ps1 b/scripts/powershell/check-prerequisites.ps1 index bb60e52c85..bd63e9094e 100644 --- a/scripts/powershell/check-prerequisites.ps1 +++ b/scripts/powershell/check-prerequisites.ps1 @@ -57,6 +57,9 @@ EXAMPLES: . "$PSScriptRoot/common.ps1" # Get feature paths +if ($PathsOnly) { + $env:SPECIFY_NO_PERSIST_FEATURE_JSON = '1' +} $paths = Get-FeaturePathsEnv # If paths-only mode, output paths and exit (no validation) diff --git a/scripts/powershell/common.ps1 b/scripts/powershell/common.ps1 index 35406d3f66..bfaf4b1025 100644 --- a/scripts/powershell/common.ps1 +++ b/scripts/powershell/common.ps1 @@ -60,6 +60,10 @@ function Save-FeatureJson { [Parameter(Mandatory = $true)][string]$FeatureDirectory ) + if ($env:SPECIFY_NO_PERSIST_FEATURE_JSON -eq '1') { + return + } + # Strip repo root prefix if the value is absolute and under repo root. # Use case-insensitive comparison on Windows only (case-sensitive filesystems elsewhere). $prefix = $RepoRoot + [System.IO.Path]::DirectorySeparatorChar diff --git a/tests/test_check_prerequisites_paths_only.py b/tests/test_check_prerequisites_paths_only.py index 03e2fc6e8b..3f376ae6ee 100644 --- a/tests/test_check_prerequisites_paths_only.py +++ b/tests/test_check_prerequisites_paths_only.py @@ -141,6 +141,33 @@ def test_paths_only_text_mode_on_non_spec_branch(prereq_repo: Path) -> None: assert "FEATURE_DIR:" in result.stdout +@requires_bash +def test_paths_only_does_not_overwrite_feature_json(prereq_repo: Path) -> None: + """--paths-only must not rewrite .specify/feature.json when env differs.""" + feat = prereq_repo / "specs" / "001-my-feature" + feat.mkdir(parents=True, exist_ok=True) + _write_feature_json(prereq_repo) + before = (prereq_repo / ".specify" / "feature.json").read_text(encoding="utf-8") + script = prereq_repo / ".specify" / "scripts" / "bash" / "check-prerequisites.sh" + env = _clean_env() + env["SPECIFY_FEATURE_DIRECTORY"] = "specs/999-temp" + result = subprocess.run( + ["bash", str(script), "--json", "--paths-only"], + cwd=prereq_repo, + capture_output=True, + text=True, + check=False, + env=env, + ) + assert result.returncode == 0, result.stderr + assert ( + (prereq_repo / ".specify" / "feature.json").read_text(encoding="utf-8") + == before + ) + data = json.loads(result.stdout) + assert data["FEATURE_DIR"].endswith("specs/999-temp") + + @requires_bash def test_normal_mode_still_validates_branch(prereq_repo: Path) -> None: """Without --paths-only, feature directory validation must still fail on main.""" @@ -211,6 +238,34 @@ def test_ps_paths_only_succeeds_on_spec_branch(prereq_repo: Path) -> None: assert "FEATURE_DIR" in data +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") +def test_ps_paths_only_does_not_overwrite_feature_json(prereq_repo: Path) -> None: + """-PathsOnly must not rewrite .specify/feature.json when env differs.""" + feat = prereq_repo / "specs" / "001-my-feature" + feat.mkdir(parents=True, exist_ok=True) + _write_feature_json(prereq_repo) + before = (prereq_repo / ".specify" / "feature.json").read_text(encoding="utf-8") + script = prereq_repo / ".specify" / "scripts" / "powershell" / "check-prerequisites.ps1" + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL + env = _clean_env() + env["SPECIFY_FEATURE_DIRECTORY"] = "specs/999-temp" + result = subprocess.run( + [exe, "-NoProfile", "-File", str(script), "-Json", "-PathsOnly"], + cwd=prereq_repo, + capture_output=True, + text=True, + check=False, + env=env, + ) + assert result.returncode == 0, result.stderr + assert ( + (prereq_repo / ".specify" / "feature.json").read_text(encoding="utf-8") + == before + ) + data = json.loads(result.stdout) + assert data["FEATURE_DIR"].replace("\\", "/").endswith("specs/999-temp") + + @pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_ps_normal_mode_still_validates_branch(prereq_repo: Path) -> None: """Without -PathsOnly, feature directory validation must still fail on main."""