Skip to content

Commit 68edf49

Browse files
author
Aaron Sun
committed
Display deprication warning during git extension setup
1 parent 56750c3 commit 68edf49

File tree

4 files changed

+66
-13
lines changed

4 files changed

+66
-13
lines changed

extensions/git/extension.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ extension:
88
author: spec-kit-core
99
repository: https://github.com/github/spec-kit
1010
license: MIT
11+
install_notice: |
12+
The git extension is currently enabled by default, but starting with
13+
v1.0.0 it will require explicit opt-in.
14+
15+
To opt in after v1.0.0:
16+
• specify init --extension git
17+
• specify extension add git (post-init)
1118
1219
requires:
1320
speckit_version: ">=0.2.0"

src/specify_cli/__init__.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,7 @@ def init(
12781278
ensure_constitution_from_template(project_path, tracker=tracker)
12791279

12801280
_git_ext_freshly_installed = False
1281+
_git_ext_install_notice: str | None = None
12811282
if not no_git:
12821283
tracker.start("git")
12831284
git_messages = []
@@ -1308,11 +1309,12 @@ def init(
13081309
if manager.registry.is_installed("git"):
13091310
git_messages.append("extension already installed")
13101311
else:
1311-
manager.install_from_directory(
1312+
ext_manifest = manager.install_from_directory(
13121313
bundled_path, get_speckit_version()
13131314
)
13141315
git_messages.append("extension installed")
13151316
_git_ext_freshly_installed = True
1317+
_git_ext_install_notice = ext_manifest.install_notice
13161318
else:
13171319
git_has_error = True
13181320
git_messages.append("bundled extension not found")
@@ -1456,16 +1458,12 @@ def init(
14561458
console.print(tracker.render())
14571459
console.print("\n[bold green]Project ready.[/bold green]")
14581460

1459-
if _git_ext_freshly_installed:
1461+
if _git_ext_freshly_installed and _git_ext_install_notice:
14601462
console.print()
14611463
console.print(
14621464
Panel(
1463-
"The [bold]git[/bold] extension is currently enabled by default, "
1464-
"but starting with [bold]v1.0.0[/bold] it will require explicit opt-in.\n\n"
1465-
"To opt in after v1.0.0:\n"
1466-
" • [cyan]specify init --extension git[/cyan]\n"
1467-
" • [cyan]specify extension add git[/cyan] (post-init)",
1468-
title="[yellow]⚠ Upcoming Change: git Extension[/yellow]",
1465+
_git_ext_install_notice.strip(),
1466+
title="[yellow]⚠ Deprecation notice: git Extension[/yellow]",
14691467
border_style="yellow",
14701468
padding=(1, 2),
14711469
)

src/specify_cli/extensions.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,15 @@ def hooks(self) -> Dict[str, Any]:
346346
"""Get hook definitions."""
347347
return self.data.get("hooks", {})
348348

349+
@property
350+
def install_notice(self) -> str | None:
351+
"""Get optional install notice message.
352+
353+
Extensions can specify an 'install_notice' field to display
354+
important information to users when the extension is first installed.
355+
"""
356+
return self.data.get("extension", {}).get("install_notice")
357+
349358
def get_hash(self) -> str:
350359
"""Calculate SHA256 hash of manifest file."""
351360
with open(self.path, 'rb') as f:

tests/extensions/git/test_git_extension.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,7 @@ def test_check_feature_branch_rejects_malformed_timestamp(self, tmp_path: Path):
800800
capture_output=True, text=True,
801801
)
802802
assert result.returncode != 0
803-
803+
804804
def test_check_feature_branch_accepts_single_prefix(self, tmp_path: Path):
805805
"""git-common check_feature_branch matches core: one optional path prefix."""
806806
project = _setup_project(tmp_path)
@@ -854,11 +854,21 @@ def test_deprecation_notice_shown_on_fresh_install(self, tmp_path: Path):
854854
project_dir = tmp_path / "test-project"
855855
runner = CliRunner()
856856

857+
mock_manifest = MagicMock()
858+
mock_manifest.install_notice = (
859+
"The git extension is currently enabled by default, but starting with\n"
860+
"v1.0.0 it will require explicit opt-in.\n\n"
861+
"To opt in after v1.0.0:\n"
862+
" • specify init --extension git\n"
863+
" • specify extension add git (post-init)"
864+
)
865+
857866
mock_registry = MagicMock()
858867
mock_registry.is_installed.return_value = False
859868

860869
mock_manager = MagicMock()
861870
mock_manager.registry = mock_registry
871+
mock_manager.install_from_directory.return_value = mock_manifest
862872

863873
with patch("specify_cli.extensions.ExtensionManager", return_value=mock_manager):
864874
result = runner.invoke(
@@ -868,9 +878,9 @@ def test_deprecation_notice_shown_on_fresh_install(self, tmp_path: Path):
868878
)
869879

870880
assert result.exit_code == 0, result.output
871-
assert "Upcoming Change: git Extension" in result.output
881+
assert "Deprecation notice: git Extension" in result.output
872882
assert "v1.0.0" in result.output
873-
assert "specify init --extension git" in result.output
883+
assert "specify extension add git" in result.output
874884

875885
def test_deprecation_notice_not_shown_when_already_installed(self, tmp_path: Path):
876886
"""specify init does NOT show the deprecation notice when git extension is already installed."""
@@ -895,7 +905,7 @@ def test_deprecation_notice_not_shown_when_already_installed(self, tmp_path: Pat
895905
)
896906

897907
assert result.exit_code == 0, result.output
898-
assert "Upcoming Change: git Extension" not in result.output
908+
assert "Deprecation notice: git Extension" not in result.output
899909

900910
def test_deprecation_notice_not_shown_with_no_git_flag(self, tmp_path: Path):
901911
"""specify init does NOT show the deprecation notice when --no-git is passed."""
@@ -912,4 +922,33 @@ def test_deprecation_notice_not_shown_with_no_git_flag(self, tmp_path: Path):
912922
)
913923

914924
assert result.exit_code == 0, result.output
915-
assert "Upcoming Change: git Extension" not in result.output
925+
assert "Deprecation notice: git Extension" not in result.output
926+
927+
def test_deprecation_notice_not_shown_when_no_install_notice(self, tmp_path: Path):
928+
"""specify init does NOT show the deprecation notice if extension has no install_notice."""
929+
from typer.testing import CliRunner
930+
from unittest.mock import patch, MagicMock
931+
from specify_cli import app
932+
933+
project_dir = tmp_path / "test-project"
934+
runner = CliRunner()
935+
936+
mock_manifest = MagicMock()
937+
mock_manifest.install_notice = None # No notice defined
938+
939+
mock_registry = MagicMock()
940+
mock_registry.is_installed.return_value = False
941+
942+
mock_manager = MagicMock()
943+
mock_manager.registry = mock_registry
944+
mock_manager.install_from_directory.return_value = mock_manifest
945+
946+
with patch("specify_cli.extensions.ExtensionManager", return_value=mock_manager):
947+
result = runner.invoke(
948+
app,
949+
["init", str(project_dir), "--ai", "claude", "--ignore-agent-tools", "--script", "sh"],
950+
catch_exceptions=False,
951+
)
952+
953+
assert result.exit_code == 0, result.output
954+
assert "Deprecation notice: git Extension" not in result.output

0 commit comments

Comments
 (0)