Skip to content

Commit 225f224

Browse files
authored
Merge pull request #19 from maxritter/claude/fix-git-setup-check-01ThyW57qRp37JuAEKL78FJY
fix: Improve and Fix new Installer
2 parents b08e945 + 431a38e commit 225f224

File tree

6 files changed

+96
-25
lines changed

6 files changed

+96
-25
lines changed

.github/workflows/quality.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ jobs:
4343
run: |
4444
python3 -m pip install --upgrade pip
4545
python3 -m pip install pytest pytest-cov
46+
python3 -m pip install rich InquirerPy httpx typer platformdirs
4647
4748
- name: Run unit tests with coverage
4849
run: |
4950
python3 -m pytest tests/unit/ -v \
50-
--cov=.claude/rules --cov=scripts \
51+
--cov=.claude/rules --cov=installer \
5152
--cov-report=term --cov-report=xml
5253
5354
- name: Run E2E tests

.github/workflows/release.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
"$HOME/.qlty/bin/qlty" check
3232
3333
build-installer-linux:
34-
name: Build Installer Linux
34+
name: Build Installer Linux ${{ matrix.arch }}
3535
runs-on: ubuntu-latest
3636
needs: quality
3737
strategy:
@@ -51,8 +51,9 @@ jobs:
5151
if: matrix.arch == 'arm64'
5252
uses: docker/setup-qemu-action@v3
5353

54-
- name: Build in Docker (GLIBC 2.31 compatible)
54+
- name: Build binary
5555
run: |
56+
# Build in Docker with Ubuntu 20.04 for GLIBC 2.31 compatibility
5657
docker run --rm \
5758
--platform linux/${{ matrix.arch }} \
5859
-v ${{ github.workspace }}:/workspace \
@@ -114,7 +115,7 @@ jobs:
114115
retention-days: 7
115116

116117
build-premium-linux:
117-
name: Build Premium Linux
118+
name: Build Premium Linux ${{ matrix.arch }}
118119
runs-on: ubuntu-latest
119120
needs: quality
120121
strategy:
@@ -146,8 +147,9 @@ jobs:
146147
git-crypt unlock /tmp/git-crypt.key
147148
rm /tmp/git-crypt.key
148149
149-
- name: Build in Docker (GLIBC 2.31 compatible)
150+
- name: Build binary
150151
run: |
152+
# Build in Docker with Ubuntu 20.04 for GLIBC 2.31 compatibility
151153
docker run --rm \
152154
--platform linux/${{ matrix.arch }} \
153155
-v ${{ github.workspace }}:/workspace \

installer/steps/git_setup.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,22 @@ def is_git_initialized(project_dir: Path) -> bool:
1818
return (project_dir / ".git").is_dir()
1919

2020

21-
def get_git_config(key: str) -> str | None:
22-
"""Get a git config value."""
21+
def get_git_config(key: str, project_dir: Path | None = None) -> str | None:
22+
"""Get a git config value (checks local repo config first, then global)."""
2323
try:
24+
# If project_dir is provided, check local config first
25+
if project_dir is not None:
26+
result = subprocess.run(
27+
["git", "config", key],
28+
capture_output=True,
29+
text=True,
30+
check=False,
31+
cwd=project_dir,
32+
)
33+
if result.returncode == 0 and result.stdout.strip():
34+
return result.stdout.strip()
35+
36+
# Fall back to global config
2437
result = subprocess.run(
2538
["git", "config", "--global", key],
2639
capture_output=True,
@@ -94,10 +107,10 @@ def check(self, ctx: InstallContext) -> bool:
94107
if not is_git_initialized(ctx.project_dir):
95108
return False
96109

97-
# Check if user config exists
98-
if not get_git_config("user.name"):
110+
# Check if user config exists (local or global)
111+
if not get_git_config("user.name", ctx.project_dir):
99112
return False
100-
if not get_git_config("user.email"):
113+
if not get_git_config("user.email", ctx.project_dir):
101114
return False
102115

103116
# Check if repo has commits

installer/ui.py

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,62 @@ def non_interactive(self) -> bool:
6666
return self._non_interactive
6767

6868
def banner(self) -> None:
69-
"""Print the Claude CodePro banner."""
70-
banner_text = Text()
71-
banner_text.append("╔═══════════════════════════════════════════════════════════╗\n", style="cyan")
72-
banner_text.append("║ ", style="cyan")
73-
banner_text.append("🚀 Claude CodePro Installer", style="bold white")
74-
banner_text.append(" ║\n", style="cyan")
75-
banner_text.append("║ ", style="cyan")
76-
banner_text.append("Professional development environment for Claude Code", style="dim white")
77-
banner_text.append(" ║\n", style="cyan")
78-
banner_text.append("╚═══════════════════════════════════════════════════════════╝", style="cyan")
79-
self._console.print(banner_text)
69+
"""Print the Claude CodePro banner with feature highlights."""
70+
# ASCII art logo
71+
logo = """
72+
[bold cyan] _____ _ _ _____ _ _____
73+
/ ____| | | | / ____| | | | __ \\
74+
| | | | __ _ _ _ __| | ___ | | ___ __| | ___| |__) | __ ___
75+
| | | |/ _` | | | |/ _` |/ _ \\| | / _ \\ / _` |/ _ \\ ___/ '__/ _ \\
76+
| |____| | (_| | |_| | (_| | __/| |___| (_) | (_| | __/ | | | | (_) |
77+
\\_____|_|\\__,_|\\__,_|\\__,_|\\___| \\_____\\___/ \\__,_|\\___|_| |_| \\___/[/bold cyan]
78+
"""
79+
self._console.print(logo)
80+
81+
# Tagline
82+
tagline = Text()
83+
tagline.append(" ✨ ", style="yellow")
84+
tagline.append("Professional Development Environment for Claude Code", style="bold white")
85+
tagline.append(" ✨", style="yellow")
86+
self._console.print(tagline, justify="center")
87+
self._console.print()
88+
89+
# Feature highlights in a styled panel
90+
features = Text()
91+
features.append(" 📋 ", style="yellow")
92+
features.append("Spec-Driven Workflow", style="bold green")
93+
features.append(" — /plan, /implement, /verify, /remember commands\n", style="white")
94+
features.append(" 💡 ", style="yellow")
95+
features.append("Context-Loaded Rules", style="bold green")
96+
features.append(" — Auto-generated standards + custom project rules\n", style="white")
97+
features.append(" 🔌 ", style="yellow")
98+
features.append("MCP Servers", style="bold green")
99+
features.append(" — Semantic search, persistent memory, web search\n", style="white")
100+
features.append(" 🛠️ ", style="yellow")
101+
features.append("Quality Automation", style="bold green")
102+
features.append(" — Post-edit hooks, linting, type checking\n", style="white")
103+
features.append(" 🏗️ ", style="yellow")
104+
features.append("Dev Container", style="bold green")
105+
features.append(" — Isolated environment with pre-configured tools\n", style="white")
106+
features.append(" 💎 ", style="yellow")
107+
features.append("Premium Features", style="bold magenta")
108+
features.append(" — AI Rules Supervisor, TDD Enforcer, Context Monitor", style="white")
109+
110+
panel = Panel(
111+
features,
112+
border_style="cyan",
113+
padding=(1, 2),
114+
title="[bold white]What You're Getting[/bold white]",
115+
title_align="left",
116+
)
117+
self._console.print(panel)
118+
119+
# Premium callout
120+
premium_text = Text()
121+
premium_text.append(" 💎 ", style="magenta")
122+
premium_text.append("Unlock premium features with a license key from ", style="dim white")
123+
premium_text.append("www.claude-code.pro", style="bold cyan underline")
124+
self._console.print(premium_text)
80125
self._console.print()
81126

82127
def set_total_steps(self, total: int) -> None:

tests/unit/installer/steps/test_git_setup.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ def test_check_returns_true_when_git_configured(self):
3434
subprocess.run(["git", "init"], cwd=project_dir, capture_output=True)
3535
subprocess.run(["git", "config", "user.name", "Test"], cwd=project_dir, capture_output=True)
3636
subprocess.run(["git", "config", "user.email", "test@test.com"], cwd=project_dir, capture_output=True)
37+
# Disable commit signing for test environments
38+
subprocess.run(["git", "config", "commit.gpgsign", "false"], cwd=project_dir, capture_output=True)
3739

3840
# Create a commit
3941
(project_dir / ".gitignore").write_text("*.tmp\n")
@@ -105,6 +107,8 @@ def test_has_commits_returns_true_with_commit(self):
105107
subprocess.run(["git", "init"], cwd=project_dir, capture_output=True)
106108
subprocess.run(["git", "config", "user.name", "Test"], cwd=project_dir, capture_output=True)
107109
subprocess.run(["git", "config", "user.email", "test@test.com"], cwd=project_dir, capture_output=True)
110+
# Disable commit signing for test environments
111+
subprocess.run(["git", "config", "commit.gpgsign", "false"], cwd=project_dir, capture_output=True)
108112
(project_dir / "test.txt").write_text("test")
109113
subprocess.run(["git", "add", "."], cwd=project_dir, capture_output=True)
110114
subprocess.run(["git", "commit", "-m", "Initial"], cwd=project_dir, capture_output=True)
@@ -150,6 +154,8 @@ def test_run_skips_when_already_configured(self):
150154
subprocess.run(["git", "init"], cwd=project_dir, capture_output=True)
151155
subprocess.run(["git", "config", "user.name", "Existing"], cwd=project_dir, capture_output=True)
152156
subprocess.run(["git", "config", "user.email", "existing@test.com"], cwd=project_dir, capture_output=True)
157+
# Disable commit signing for test environments
158+
subprocess.run(["git", "config", "commit.gpgsign", "false"], cwd=project_dir, capture_output=True)
153159
(project_dir / ".gitignore").write_text("*.tmp\n")
154160
subprocess.run(["git", "add", "."], cwd=project_dir, capture_output=True)
155161
subprocess.run(["git", "commit", "-m", "Initial"], cwd=project_dir, capture_output=True)

tests/unit/installer/test_base.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@ def test_step_protocol_has_required_methods(self):
1717
"""Step protocol must define check, run, rollback methods."""
1818
from installer.steps.base import Step
1919

20-
# Check protocol annotations define required methods
21-
assert "check" in Step.__protocol_attrs__
22-
assert "run" in Step.__protocol_attrs__
23-
assert "rollback" in Step.__protocol_attrs__
20+
# Check protocol defines required methods (compatible with Python 3.10+)
21+
# Use annotations or __dict__ to check for method definitions
22+
protocol_methods = set(Step.__annotations__.keys()) | set(
23+
k for k in dir(Step) if not k.startswith("_") and callable(getattr(Step, k, None))
24+
)
25+
assert "check" in protocol_methods
26+
assert "run" in protocol_methods
27+
assert "rollback" in protocol_methods
2428

2529
def test_base_step_is_instance_of_step_protocol(self):
2630
"""BaseStep instance must satisfy Step protocol."""

0 commit comments

Comments
 (0)