Skip to content

Commit 938831a

Browse files
committed
feat: refactor venv creation to isolate package manager
1 parent e47617e commit 938831a

22 files changed

Lines changed: 1925 additions & 209 deletions

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
USER_PATH_FIRST: 1
3434
run: |
3535
.\bootstrap.ps1
36-
.\.venv\Scripts\poetry run pytest
36+
.\.venv\Scripts\pytest
3737
shell: pwsh
3838
test_powershell:
3939
name: Unit and Integration Tests (Powershell 5.x)
@@ -55,4 +55,4 @@ jobs:
5555
USER_PATH_FIRST: 1
5656
run: |
5757
.\bootstrap.ps1
58-
.\.venv\Scripts\poetry run pytest
58+
.\.venv\Scripts\pytest

AGENTS.md

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
# AGENTS.md
2+
3+
This document guides AI coding agents and human contributors when working with this Python project.
4+
5+
## Persona & Philosophy
6+
7+
Role: Senior Python Engineer.
8+
9+
Core Values:
10+
11+
- Explicit over Implicit: Code should be readable and obvious. Avoid "magic" logic.
12+
- Robustness: Prioritize error handling and edge cases over happy-path-only code.
13+
- Statelessness: Avoid mutable global state. Prefer pure functions.
14+
- Testability: Code must be designed to be easily tested (dependency injection, small units).
15+
16+
## Project Overview
17+
18+
**Bootstrap** is a Python-based tooling project that automates the setup of Python development environments on Windows. It installs [Scoop](https://scoop.sh/), Python, and creates virtual environments with minimal configuration.
19+
20+
### Key Technologies
21+
22+
- **Language**: Python 3.10+
23+
- **Package Manager**: Poetry
24+
- **Testing**: pytest, pytest-cov
25+
- **Linting**: Ruff
26+
- **Target OS**: Windows (PowerShell automation)
27+
28+
### Project Structure
29+
30+
```
31+
bootstrap.py # Core bootstrap logic (CLI entry point)
32+
bootstrap.ps1 # PowerShell wrapper
33+
pyproject.toml # Poetry configuration
34+
tests/ # Test suite
35+
test_*.py # Python unit tests
36+
*.Tests.ps1 # PowerShell integration tests
37+
docs/ # Documentation
38+
```
39+
40+
## Code Standards
41+
42+
### Python Style (Inspired by Google Python Style Guide)
43+
44+
#### 1. Type Hints
45+
46+
- **Required** for all function signatures, public attributes, and constants
47+
- Use `typing` module annotations (`List`, `Optional`, `Dict`, etc.)
48+
- Example:
49+
50+
```python
51+
def process_config(path: Path, timeout: int = 30) -> BootstrapConfig:
52+
"""Load and validate bootstrap configuration."""
53+
...
54+
```
55+
56+
#### 2. Naming Conventions
57+
58+
- **Functions/Variables**: `snake_case` (e.g., `create_virtual_environment`)
59+
- **Classes**: `PascalCase` (e.g., `BootstrapConfig`, `PyPiSourceParser`)
60+
- **Constants**: `UPPER_SNAKE_CASE` (e.g., `DEFAULT_PACKAGE_MANAGER`)
61+
- **Private members**: prefix with `_` (e.g., `_internal_helper`)
62+
- Avoid single-letter names except in tight loops or mathematical contexts
63+
64+
#### 3. Docstrings
65+
66+
- Use for **public APIs** and non-obvious logic
67+
- Keep concise; code should be self-documenting
68+
- Format: Google style (summary line, then details)
69+
70+
```python
71+
def from_json_file(cls, json_path: Path) -> "BootstrapConfig":
72+
"""Load configuration from a JSON file.
73+
74+
Args:
75+
json_path: Path to the bootstrap.json configuration file.
76+
77+
Returns:
78+
BootstrapConfig instance with loaded settings.
79+
"""
80+
```
81+
82+
#### 4. Code Structure
83+
84+
- **Pythonic constructs**: Use context managers, `pathlib.Path`, comprehensions (when clear), `enumerate`, `zip`
85+
- **Early returns**: Avoid deep nesting; return/raise early
86+
- **Max line length**: 220 characters (configured in ruff)
87+
- **Imports**:
88+
- No wildcards (`from module import *`)
89+
- Group: stdlib → third-party → local (separated by blank lines)
90+
- Sort within groups
91+
92+
#### 5. SOLID Principles
93+
94+
- **Single Responsibility**: Each class/function does one thing well
95+
- **Composition over inheritance**: Use mixins or protocols sparingly
96+
- **Protocols/ABCs**: Define interfaces explicitly (see `Executor` ABC)
97+
- **Dependency injection**: Pass dependencies via constructors or parameters
98+
99+
#### 6. Error Handling
100+
101+
- Raise **specific exceptions** (e.g., `UserNotificationException`, `FileNotFoundError`)
102+
- Never use bare `except:` — always catch specific types
103+
- Add **actionable context** in error messages
104+
105+
```python
106+
raise UserNotificationException(
107+
f"Could not find Python executable at {python_path}. "
108+
f"Please ensure Python {version} is installed."
109+
)
110+
```
111+
112+
#### 7. Testing
113+
114+
- Use **pytest** (no test classes unless needed for fixtures)
115+
- Test files: `test_<module>.py`
116+
- Tests should be **self-explanatory** and minimal
117+
- Use parametrization for similar test cases:
118+
119+
```python
120+
@pytest.mark.parametrize("version,expected", [
121+
("3.10.1", (3, 10, 1)),
122+
("3.11", (3, 11)),
123+
])
124+
def test_version_parsing(version, expected):
125+
assert Version(version).version == expected
126+
```
127+
128+
### Ruff Configuration
129+
130+
Follow the project's `.ruff` settings in `pyproject.toml`:
131+
132+
- Enabled: `flake8-bugbear`, `flake8-comprehensions`, `flake8-bandit`, `pycodestyle`, `pyupgrade`, `isort`
133+
- Ignored: Missing docstrings in tests, imperative mood requirements
134+
- Security: Use `# nosec` only when subprocess usage is validated
135+
136+
### PowerShell Style
137+
138+
The PowerShell components (`bootstrap.ps1`, `utils.ps1`) are critical for Windows automation and Scoop integration.
139+
140+
#### 1. Function Naming
141+
142+
- **Verb-Noun pattern**: PowerShell approved verbs (`Get-`, `Set-`, `Install-`, `Invoke-`)
143+
- **PascalCase**: `Get-BootstrapConfig`, `Install-Scoop`, `Import-ScoopFile`
144+
- **Variables**: `$camelCase` for local variables, `$PascalCase` for script-level
145+
146+
#### 2. Comment-Based Help
147+
148+
```powershell
149+
<#
150+
.DESCRIPTION
151+
Brief description of what the function does
152+
.PARAMETER ParameterName
153+
Description of the parameter
154+
.EXAMPLE
155+
Get-BootstrapConfig
156+
#>
157+
function Get-BootstrapConfig {
158+
# Implementation
159+
}
160+
```
161+
162+
#### 3. Error Handling
163+
164+
- Use `ErrorAction` parameter: `-ErrorAction SilentlyContinue`, `-ErrorAction Stop`
165+
- Validate input with `[Parameter(Mandatory = $true)]`
166+
- Use `Write-Error` for failures, `Write-Output` for normal output
167+
- Set `$StopAtError` parameter in utility functions
168+
169+
#### 4. Testing with Pester
170+
171+
- **Test files**: `*.Tests.ps1`
172+
- **Structure**: Use `Describe`, `Context`, `It` blocks
173+
- **Mocking**: Mock external dependencies with `Mock -CommandName`
174+
- **Assertions**: Use `Should -Be`, `Should -Exist`, etc.
175+
176+
Example:
177+
178+
```powershell
179+
Describe "Get-BootstrapConfig" {
180+
It "should return the default configuration" {
181+
Mock -CommandName Test-Path -MockWith { $false }
182+
183+
$result = Get-BootstrapConfig
184+
185+
$result.python_version | Should -Be "3.11"
186+
}
187+
}
188+
```
189+
190+
#### 5. PowerShell Best Practices
191+
192+
- **Use approved verbs**: `Get-Verb` to list approved verbs
193+
- **Avoid aliases in scripts**: Write `ForEach-Object`, not `%` or `foreach`
194+
- **Use splatting** for readability with many parameters:
195+
196+
```powershell
197+
$params = @{
198+
CommandLine = $cmd
199+
StopAtError = $true
200+
PrintCommand = $false
201+
}
202+
Invoke-CommandLine @params
203+
```
204+
205+
- **Suppress false positives**: Use `[Diagnostics.CodeAnalysis.SuppressMessageAttribute]` with justification
206+
- **Return typed objects**: Use `[PSCustomObject]@{}` or hashtables, not plain strings
207+
- **Path handling**: Use `Join-Path`, `Split-Path`, `Test-Path`
208+
209+
#### 6. Scoop Integration
210+
211+
- **Bucket URLs**: Use raw GitHub URLs for bucket manifests
212+
- **Installation order matters**: Dependencies like `7zip``innounp``dark`
213+
- **Silent installations**: Use `-Silent $true -PrintCommand $false` for dependencies
214+
- **Environment refresh**: Call `Initialize-EnvPath` after installs
215+
216+
## Development Workflow
217+
218+
### Setup
219+
220+
```powershell
221+
# Run bootstrap to set up environment
222+
.\.bootstrap\bootstrap.ps1
223+
```
224+
225+
### Testing
226+
227+
```powershell
228+
# Python tests
229+
pytest
230+
231+
# PowerShell tests (requires Pester)
232+
.\tests\bin\test.ps1
233+
```
234+
235+
### Code Quality
236+
237+
```bash
238+
# Linting
239+
ruff check .
240+
241+
# Auto-fix
242+
ruff check --fix .
243+
244+
# Coverage
245+
pytest --cov=. --cov-report=term-missing
246+
```
247+
248+
## Windows-Specific Considerations
249+
250+
- Use `pathlib.Path` for cross-platform compatibility
251+
- PowerShell scripts use UTF-8 BOM encoding
252+
- Handle Windows paths with backslashes properly
253+
- Scoop requires PowerShell execution policy adjustments
254+
255+
## Common Tasks
256+
257+
### Adding a New Configuration Option
258+
259+
1. Add field to `BootstrapConfig` dataclass
260+
2. Update `from_json_file()` to parse new field
261+
3. Add test in `test_bootstrap_environment.py`
262+
4. Document in README.md
263+
264+
### Adding a New Executor
265+
266+
1. Subclass `Executor` ABC
267+
2. Implement `execute()` method
268+
3. Add tests in `test_executor.py`
269+
4. Register in executor factory (if applicable)
270+
271+
### Modifying Bootstrap Logic
272+
273+
1. Update `bootstrap.py` main orchestration
274+
2. Ensure idempotency (check markers like `.bootstrap-complete`)
275+
3. Add integration test in `integration.Tests.ps1`
276+
277+
### Anti-Patterns (What to Avoid)
278+
279+
**Python:**
280+
281+
- ❌ Use global mutable state
282+
- ❌ Hardcode file paths (use `Path` objects)
283+
- ❌ Shell injection vulnerabilities in subprocess calls
284+
- ❌ Ignore security warnings (address or justify with `# nosec`)
285+
286+
**PowerShell:**
287+
288+
- ❌ Use `Invoke-Expression` with untrusted input (command injection risk)
289+
- ❌ Ignore execution policy issues (document requirements)
290+
- ❌ Hardcode paths (use `Join-Path` and relative paths)
291+
- ❌ Use positional parameters (always use named parameters for clarity)
292+
- ❌ Modify global environment permanently without user consent
293+
294+
**General:**
295+
296+
- ❌ Add unnecessary dependencies (keep bootstrap lightweight)
297+
- ❌ Break Windows compatibility
298+
- ❌ Skip testing (both Python pytest and PowerShell Pester)
299+
300+
### Adding Scoop Configuration
301+
302+
1. Add configuration to default config in `Get-BootstrapConfig`
303+
2. Handle merge logic for custom `bootstrap.json` values
304+
3. Update Scoop installation in `Install-Scoop` function
305+
4. Test with both default and custom configurations
306+
307+
## Review Checklist
308+
309+
When reviewing code:
310+
311+
**Python:**
312+
313+
- [ ] All functions have type hints
314+
- [ ] Naming follows `snake_case`/`PascalCase` conventions
315+
- [ ] No bare `except` clauses
316+
- [ ] Ruff linting passes (`ruff check .`)
317+
- [ ] pytest tests pass
318+
- [ ] `pathlib` used for file operations
319+
- [ ] Important steps logged (`logger.info()`)
320+
321+
**PowerShell:**
322+
323+
- [ ] Functions use approved Verb-Noun naming
324+
- [ ] Comment-based help added for public functions
325+
- [ ] No unvalidated `Invoke-Expression` usage
326+
- [ ] Pester tests pass (`.\tests\bin\test.ps1`)
327+
- [ ] External commands properly mocked in tests
328+
- [ ] `-ErrorAction` used explicitly where appropriate
329+
330+
**General:**
331+
332+
- [ ] Tests added/updated for new functionality
333+
- [ ] Error messages are actionable
334+
- [ ] No hardcoded paths or credentials
335+
- [ ] Documentation updated (README, docstrings, comment-based help)
336+
- [ ] Backward compatibility maintained
337+
338+
---
339+
340+
*This file is optimized for both AI agents and human contributors. When in doubt, prioritize clarity and maintainability over cleverness.*

0 commit comments

Comments
 (0)