docs(06): create phase plan for wiring icon into intunewin export
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,170 @@
|
|||||||
|
---
|
||||||
|
phase: 06-wire-icon-intunewin
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- imptune/api/packages.py
|
||||||
|
- tests/test_packages.py
|
||||||
|
autonomous: true
|
||||||
|
requirements:
|
||||||
|
- PKG-04
|
||||||
|
must_haves:
|
||||||
|
truths:
|
||||||
|
- "Exported .intunewin includes icon.png in staging when printer has an uploaded icon"
|
||||||
|
- "Exported .intunewin succeeds without error when printer has no icon"
|
||||||
|
artifacts:
|
||||||
|
- path: "imptune/api/packages.py"
|
||||||
|
provides: "Icon lookup and copy into tmpdir staging"
|
||||||
|
contains: "Icon.get_or_none"
|
||||||
|
- path: "tests/test_packages.py"
|
||||||
|
provides: "Integration tests for icon-in-package and no-icon baseline"
|
||||||
|
contains: "test_intunewin_includes_icon"
|
||||||
|
key_links:
|
||||||
|
- from: "imptune/api/packages.py"
|
||||||
|
to: "imptune/db/models.py"
|
||||||
|
via: "Icon.get_or_none(Icon.printer == printer.id)"
|
||||||
|
pattern: "Icon\\.get_or_none"
|
||||||
|
- from: "imptune/api/packages.py"
|
||||||
|
to: "imptune/config.py"
|
||||||
|
via: "cfg.ICONS_DIR for icon source path"
|
||||||
|
pattern: "cfg\\.ICONS_DIR"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
Wire the uploaded PNG icon into the .intunewin export pipeline so that printers with an uploaded icon include it in the deployment package.
|
||||||
|
|
||||||
|
Purpose: Closes the PKG-04 gap — icon upload exists (Phase 5) but the .intunewin builder never receives the icon file. This is the last unsatisfied v1 requirement.
|
||||||
|
Output: Modified packages.py with icon lookup + copy, two new integration tests in test_packages.py.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.planning/PROJECT.md
|
||||||
|
@.planning/ROADMAP.md
|
||||||
|
@.planning/STATE.md
|
||||||
|
@.planning/phases/06-wire-icon-intunewin/06-RESEARCH.md
|
||||||
|
|
||||||
|
<interfaces>
|
||||||
|
<!-- Key types and contracts the executor needs. -->
|
||||||
|
|
||||||
|
From imptune/db/models.py:
|
||||||
|
```python
|
||||||
|
class Icon(BaseModel):
|
||||||
|
printer = ForeignKeyField(Printer, unique=True, backref="icons")
|
||||||
|
sha256 = CharField()
|
||||||
|
original_filename = CharField()
|
||||||
|
size_bytes = IntegerField()
|
||||||
|
uploaded_at = DateTimeField(default=datetime.utcnow)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
table_name = "icon"
|
||||||
|
```
|
||||||
|
|
||||||
|
From imptune/config.py:
|
||||||
|
```python
|
||||||
|
ICONS_DIR = str(Path(DATA_DIR) / "icons")
|
||||||
|
```
|
||||||
|
|
||||||
|
From imptune/api/packages.py (insertion point — line 146, after driver extraction, before build_intunewin):
|
||||||
|
```python
|
||||||
|
# Line 132: with tempfile.TemporaryDirectory(prefix="imptune_") as tmpdir:
|
||||||
|
# Lines 134-139: write install.ps1, uninstall.ps1, detect.ps1
|
||||||
|
# Lines 142-145: extract driver ZIP into tmpdir/drivers/
|
||||||
|
# >>> INSERT ICON COPY HERE <<<
|
||||||
|
# Line 148: output_path = os.path.join(tmpdir, "out.intunewin")
|
||||||
|
# Line 149: build_intunewin(tmpdir, "install.ps1", output_path)
|
||||||
|
```
|
||||||
|
|
||||||
|
From tests/test_packages.py (existing fixtures):
|
||||||
|
```python
|
||||||
|
@pytest.fixture
|
||||||
|
def setup_printer_with_driver(tmp_data_dir, driver_zip_bytes):
|
||||||
|
# Creates Driver + Printer records, writes driver ZIP to cfg.DRIVERS_DIR
|
||||||
|
# Returns (printer, driver)
|
||||||
|
```
|
||||||
|
</interfaces>
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto" tdd="true">
|
||||||
|
<name>Task 1: Add icon-in-package tests and wire icon into packages.py</name>
|
||||||
|
<files>tests/test_packages.py, imptune/api/packages.py</files>
|
||||||
|
<behavior>
|
||||||
|
- test_intunewin_includes_icon: upload a 256x256 PNG icon for the printer, monkeypatch build_intunewin to capture staged file list, call GET /printers/{id}/packages/intunewin, assert "icon.png" is in staged files and response is 200
|
||||||
|
- test_intunewin_without_icon_succeeds: no icon uploaded, monkeypatch build_intunewin, call GET /printers/{id}/packages/intunewin, assert response is 200 (no crash from missing icon)
|
||||||
|
</behavior>
|
||||||
|
<action>
|
||||||
|
RED phase — add two tests to TestIntunewinDownload class in tests/test_packages.py:
|
||||||
|
|
||||||
|
1. `test_intunewin_includes_icon(self, client, setup_printer_with_driver, tmp_data_dir, monkeypatch)`:
|
||||||
|
- Create a 256x256 RGBA PNG using `PIL.Image.new("RGBA", (256, 256), color="red")`
|
||||||
|
- POST it to `/printers/{printer.id}/icon` as multipart file upload
|
||||||
|
- Assert upload returns 200
|
||||||
|
- Define `fake_build(source_dir, setup_file, output_path)` that captures `os.listdir(source_dir)` into a list and writes `b"FAKE"` to output_path
|
||||||
|
- Monkeypatch `imptune.api.packages.build_intunewin` with fake_build
|
||||||
|
- GET `/printers/{printer.id}/packages/intunewin`
|
||||||
|
- Assert status 200 and `"icon.png"` in captured staged files
|
||||||
|
|
||||||
|
2. `test_intunewin_without_icon_succeeds(self, client, setup_printer_with_driver, monkeypatch)`:
|
||||||
|
- Same fake_build monkeypatch (no icon upload)
|
||||||
|
- GET `/printers/{printer.id}/packages/intunewin`
|
||||||
|
- Assert status 200
|
||||||
|
|
||||||
|
Run tests — both MUST fail (icon.png not staged, and second test should actually pass since no icon code yet — if it passes, that is acceptable for the baseline).
|
||||||
|
|
||||||
|
GREEN phase — modify imptune/api/packages.py:
|
||||||
|
|
||||||
|
1. Add `import shutil` at top
|
||||||
|
2. Add `from imptune.db.models import Icon` to the existing models import (line 12 area — add Icon next to Printer)
|
||||||
|
3. Inside `get_intunewin_package()`, after the driver ZIP extraction block (after line 145) and BEFORE `build_intunewin()` (line 149), insert:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Copy icon into staging if one exists for this printer
|
||||||
|
icon_record = Icon.get_or_none(Icon.printer == printer.id)
|
||||||
|
if icon_record is not None:
|
||||||
|
icon_src = os.path.join(cfg.ICONS_DIR, icon_record.sha256)
|
||||||
|
if os.path.isfile(icon_src):
|
||||||
|
shutil.copy2(icon_src, os.path.join(tmpdir, "icon.png"))
|
||||||
|
```
|
||||||
|
|
||||||
|
This is 4 lines of production code. The icon is optional — missing DB record or missing file on disk both result in silent skip (no error, export proceeds without icon).
|
||||||
|
|
||||||
|
Run tests again — both MUST pass.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>python -m pytest tests/test_packages.py -x -q</automated>
|
||||||
|
</verify>
|
||||||
|
<done>
|
||||||
|
- test_intunewin_includes_icon passes: icon.png present in staged files when icon uploaded
|
||||||
|
- test_intunewin_without_icon_succeeds passes: export works with no icon
|
||||||
|
- All pre-existing test_packages.py tests still pass (no regressions)
|
||||||
|
- Full test suite green: python -m pytest tests/ -q
|
||||||
|
</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
- `python -m pytest tests/test_packages.py -x -q` — all tests pass including two new icon tests
|
||||||
|
- `python -m pytest tests/ -q` — full suite green, no regressions
|
||||||
|
- Manual code review: `shutil.copy2` call is BEFORE `build_intunewin()` call (not after)
|
||||||
|
- Manual code review: `Icon` import added, `cfg.ICONS_DIR` used (not hardcoded path)
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
1. Printers with an uploaded icon have icon.png included in .intunewin staging directory
|
||||||
|
2. Printers without an icon export successfully with no error
|
||||||
|
3. All existing tests pass without modification
|
||||||
|
4. PKG-04 requirement satisfied
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/06-wire-icon-intunewin/06-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
Reference in New Issue
Block a user