Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.4 KiB
9.4 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 04-script-generation | 01 | tdd | 1 |
|
true |
|
|
Purpose: The install script is the most complex of the three scripts (WOW64, UAC, pnputil two-step, idempotency, duplex mapping). Building it first with TDD ensures all edge cases are covered before the simpler templates.
Output: script_generator.py with render_install(), install.ps1.j2 template, and passing unit tests.
<execution_context> @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/04-script-generation/04-RESEARCH.mdFrom imptune/db/models.py:
class Printer(BaseModel):
name = CharField()
ip_address = CharField()
port_name = CharField()
client = ForeignKeyField(Client, null=True, backref="printers")
driver = ForeignKeyField(Driver, null=True, backref="printers")
duplex_mode = CharField(default="OneSided") # "OneSided" | "LongEdge" | "ShortEdge"
color_mode = BooleanField(default=True)
paper_size = CharField(default="A4") # "A4" | "Letter" | "Legal"
collate = BooleanField(default=True)
class Driver(BaseModel):
sha256 = CharField(unique=True, index=True)
original_filename = CharField()
driver_desc = CharField(null=True) # JSON list: '["HP Universal Printing PCL 6"]'
inf_filename = CharField(null=True) # e.g. "hpcu270u.inf"
From imptune/generators/intunewin_builder.py (pattern reference):
# Existing generator module pattern — script_generator.py follows same structure
# Module-level setup, public render functions
1. Create `imptune/templates/scripts/` directory (empty, needed for Jinja2 loader)
2. Write tests that import `render_install` from `imptune.generators.script_generator` and assert on rendered output:
- `test_render_install_contains_pnputil`: assert `pnputil.exe /add-driver` and `Add-PrinterDriver` in output
- `test_render_install_print_config`: assert `Set-PrintConfiguration` with `-DuplexingMode TwoSidedLongEdge` when duplex_mode="LongEdge"
- `test_render_install_wow64_guard`: assert `PROCESSOR_ARCHITECTURE` and `SysNative` in output
- `test_render_install_uac_guard`: assert `IsSystem` and `Start-Process` and `-Verb Runas` in output
- `test_render_install_idempotency`: assert `Get-PrinterPort` and `Get-Printer` checks before add operations
- `test_render_install_booleans`: assert `$true` / `$false` for color and collate
3. Run tests — all MUST fail (RED)
**GREEN phase — implement:**
4. Create `imptune/generators/script_generator.py`:
- Module-level Jinja2 Environment with `FileSystemLoader` pointing to `imptune/templates/scripts/`
- `trim_blocks=True`, `lstrip_blocks=True`, `keep_trailing_newline=True`
- `_duplex_map` dict: `{"OneSided": "OneSided", "LongEdge": "TwoSidedLongEdge", "ShortEdge": "TwoSidedShortEdge"}`
- `render_install(printer_name, ip_address, port_name, driver_name, inf_filename, duplex_mode, color_mode, paper_size, collate) -> str`
- Translates duplex_mode via `_duplex_map`
- Converts color_mode/collate bools to `"true"` / `"false"` (lowercase, template adds `$` prefix)
- Calls `_env.get_template("install.ps1.j2").render(...)` with all variables
- Use plain string parameters (not ORM objects) so the function is testable without DB
5. Create `imptune/templates/scripts/install.ps1.j2`:
Template structure (in this exact order):
```
# Header comment: Generated by ImpTune, printer name, install command hint
# WOW64 Guard (FIRST executable block)
if ($env:PROCESSOR_ARCHITECTURE -eq "x86" -and $env:PROCESSOR_ARCHITEW6432) { ... relaunch 64-bit ... exit }
# SYSTEM/Admin check + UAC elevation
$id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$isSystem = $id.IsSystem
$isAdmin = ... IsInRole(Administrator)
if (-not $isSystem -and -not $isAdmin) { Start-Process -Verb Runas ... exit }
# pnputil driver staging
pnputil.exe /add-driver "$PSScriptRoot\drivers\{{ inf_filename }}" /install
# Add-PrinterDriver
Add-PrinterDriver -Name "{{ driver_name }}"
# Idempotent port creation
if (-not (Get-PrinterPort -Name "{{ port_name }}" -ErrorAction SilentlyContinue)) { Add-PrinterPort ... }
# Idempotent printer creation
if (-not (Get-Printer -Name "{{ printer_name }}" -ErrorAction SilentlyContinue)) { Add-Printer ... }
# Set-PrintConfiguration
Set-PrintConfiguration -PrinterName "{{ printer_name }}" -DuplexingMode {{ duplex_mode }} -Color ${{ color }} -PaperSize {{ paper_size }} -Collate ${{ collate }}
```
6. Run tests — all MUST pass (GREEN)
**Important notes:**
- Use plain string args for render_install, NOT Printer ORM object — keeps tests DB-free
- The `_duplex_map` must translate BEFORE passing to template (template receives already-mapped value)
- Boolean values: pass as lowercase string `"true"` / `"false"` so template renders `$true` / `$false` with `${{ color }}`
- Template must use `{{ }}` for all variable interpolation — no `{% set %}` for simple values
- All PowerShell string parameters (printer_name, port_name, driver_name) must be double-quoted in the template
<success_criteria>
- render_install() produces complete PowerShell install script
- WOW64 guard appears before any other logic
- UAC self-elevation skips when SYSTEM
- Duplex mode values correctly translated (LongEdge -> TwoSidedLongEdge)
- All add operations wrapped in idempotency checks
- All unit tests pass </success_criteria>