feat(05-02): printer detail page with export buttons and command preview
- Update printer_detail() to pass install_cmd, uninstall_cmd, has_driver, has_icon to template - Rewrite printer_detail.html with Intune Commands, Export, and Icon sections - Show command strings with Alpine.js copy-to-clipboard buttons (install-cmd, uninstall-cmd) - Show NinjaRMM ZIP and .intunewin download links when driver assigned - Add HTMX icon upload form with #icon-status swap target - Remove disabled placeholder Regenerate Package button - Add TestCommandPreview class (4 tests) to test_packages.py - All 94 tests pass
This commit is contained in:
@@ -38,9 +38,9 @@ Requirements for initial release. Each maps to roadmap phases.
|
||||
|
||||
### Package Export
|
||||
|
||||
- [ ] **PKG-01**: User can export a complete .intunewin package (script + drivers + detection + metadata)
|
||||
- [ ] **PKG-02**: .intunewin is generated natively in Python (no IntuneWinAppUtil.exe dependency)
|
||||
- [ ] **PKG-03**: User can export a NinjaRMM ZIP package (install script + driver folder)
|
||||
- [x] **PKG-01**: User can export a complete .intunewin package (script + drivers + detection + metadata)
|
||||
- [x] **PKG-02**: .intunewin is generated natively in Python (no IntuneWinAppUtil.exe dependency)
|
||||
- [x] **PKG-03**: User can export a NinjaRMM ZIP package (install script + driver folder)
|
||||
- [ ] **PKG-04**: User can upload a custom PNG icon for Intune app display (256x256, max 750KB)
|
||||
- [ ] **PKG-05**: User can preview and copy Intune install/uninstall command strings before export
|
||||
|
||||
@@ -105,9 +105,9 @@ Which phases cover which requirements. Updated during roadmap creation.
|
||||
| SCRPT-03 | Phase 4 | Complete |
|
||||
| SCRPT-04 | Phase 4 | Complete |
|
||||
| SCRPT-05 | Phase 4 | Complete |
|
||||
| PKG-01 | Phase 5 | Pending |
|
||||
| PKG-02 | Phase 5 | Pending |
|
||||
| PKG-03 | Phase 5 | Pending |
|
||||
| PKG-01 | Phase 5 | Complete |
|
||||
| PKG-02 | Phase 5 | Complete |
|
||||
| PKG-03 | Phase 5 | Complete |
|
||||
| PKG-04 | Phase 5 | Pending |
|
||||
| PKG-05 | Phase 5 | Pending |
|
||||
|
||||
|
||||
@@ -108,4 +108,4 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5
|
||||
| 2. Driver Management | 1/2 | In Progress| |
|
||||
| 3. Printer Configuration | 2/2 | Complete | 2026-04-10 |
|
||||
| 4. Script Generation | 2/2 | Complete | 2026-04-10 |
|
||||
| 5. Package Export | 0/2 | Not started | - |
|
||||
| 5. Package Export | 1/2 | In Progress| |
|
||||
|
||||
+10
-6
@@ -3,14 +3,14 @@ gsd_state_version: 1.0
|
||||
milestone: v1.0
|
||||
milestone_name: milestone
|
||||
status: executing
|
||||
stopped_at: Completed 04-02-PLAN.md
|
||||
last_updated: "2026-04-10T11:40:40.706Z"
|
||||
stopped_at: Completed 05-01-PLAN.md
|
||||
last_updated: "2026-04-10T13:06:29.301Z"
|
||||
last_activity: "2026-04-10 — Plan 04-02 complete: uninstall/detect templates, render_uninstall/render_detect, 3 script API endpoints, 75 tests green"
|
||||
progress:
|
||||
total_phases: 5
|
||||
completed_phases: 4
|
||||
total_plans: 9
|
||||
completed_plans: 9
|
||||
total_plans: 11
|
||||
completed_plans: 10
|
||||
---
|
||||
|
||||
---
|
||||
@@ -72,6 +72,7 @@ Progress: [█████████░] 89%
|
||||
| Phase 03-printer-configuration P02 | 2 | 2 tasks | 4 files |
|
||||
| Phase 04-script-generation P01 | 2 | 1 tasks | 3 files |
|
||||
| Phase 04-script-generation P02 | 2 | 2 tasks | 6 files |
|
||||
| Phase 05-package-export P01 | 2 | 1 tasks | 3 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -104,6 +105,9 @@ Recent decisions affecting current work:
|
||||
- [Phase 04-02]: _get_printer_and_driver() private helper centralises 404/422 validation for all 3 script endpoints
|
||||
- [Phase 04-02]: PlainTextResponse with Content-Disposition attachment header on all script endpoints
|
||||
- [Phase 04-02]: Integration tests create ORM records directly (Driver.create/Printer.create)
|
||||
- [Phase 05-package-export]: _get_printer_and_driver() copied (not imported) from scripts.py for module self-containment in packages.py
|
||||
- [Phase 05-package-export]: NinjaRMM ZIP uses DEFLATE compression with {printer_name}/install.ps1 + {printer_name}/drivers/* structure
|
||||
- [Phase 05-package-export]: intunewin endpoint uses TemporaryDirectory for auto-cleanup; driver ZIP existence validated before processing (422 if missing)
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -116,6 +120,6 @@ None yet.
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-04-10T11:37:48.305Z
|
||||
Stopped at: Completed 04-02-PLAN.md
|
||||
Last session: 2026-04-10T13:06:29.293Z
|
||||
Stopped at: Completed 05-01-PLAN.md
|
||||
Resume file: None
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
phase: 05-package-export
|
||||
plan: "01"
|
||||
subsystem: api/packages
|
||||
tags: [fastapi, zip, intunewin, package-export, tdd]
|
||||
dependency_graph:
|
||||
requires:
|
||||
- imptune/generators/script_generator.py (render_install, render_uninstall, render_detect)
|
||||
- imptune/generators/intunewin_builder.py (build_intunewin)
|
||||
- imptune/api/scripts.py (_get_printer_and_driver pattern)
|
||||
- imptune/db/models.py (Printer, Driver ORM)
|
||||
- imptune/config.py (DRIVERS_DIR)
|
||||
provides:
|
||||
- GET /printers/{id}/packages/ninja (NinjaRMM ZIP download)
|
||||
- GET /printers/{id}/packages/intunewin (.intunewin download)
|
||||
affects:
|
||||
- imptune/main.py (router registration)
|
||||
tech_stack:
|
||||
added: []
|
||||
patterns:
|
||||
- In-memory ZIP assembly with io.BytesIO + zipfile.ZipFile
|
||||
- TemporaryDirectory context manager for auto-cleanup of intunewin build artifacts
|
||||
- Driver ZIP existence validation before processing
|
||||
key_files:
|
||||
created:
|
||||
- imptune/api/packages.py
|
||||
- tests/test_packages.py
|
||||
modified:
|
||||
- imptune/main.py
|
||||
decisions:
|
||||
- _get_printer_and_driver() copied (not imported) from scripts.py for module self-containment
|
||||
- NinjaRMM ZIP uses DEFLATE compression with {printer_name}/install.ps1 + {printer_name}/drivers/* structure
|
||||
- intunewin endpoint uses TemporaryDirectory for auto-cleanup of tmp build files (no manual cleanup needed)
|
||||
- Driver ZIP file existence validated on disk before building package (422 if missing)
|
||||
metrics:
|
||||
duration: "~2 min"
|
||||
completed_date: "2026-04-10"
|
||||
tasks_completed: 1
|
||||
files_modified: 3
|
||||
---
|
||||
|
||||
# Phase 5 Plan 1: Package Export Endpoints Summary
|
||||
|
||||
**One-liner:** NinjaRMM ZIP and .intunewin package export endpoints using in-memory ZIP assembly and Python-native intunewin build.
|
||||
|
||||
## What Was Built
|
||||
|
||||
Two GET endpoints on `imptune/api/packages.py`:
|
||||
|
||||
1. **`GET /printers/{id}/packages/ninja`** — Returns a ZIP file (application/zip) with:
|
||||
- `{safe_name}/install.ps1` — rendered PowerShell install script
|
||||
- `{safe_name}/drivers/*` — all driver files extracted from the driver ZIP on disk
|
||||
- Built entirely in-memory with `io.BytesIO` + `zipfile.ZipFile(ZIP_DEFLATED)`
|
||||
|
||||
2. **`GET /printers/{id}/packages/intunewin`** — Returns a `.intunewin` file (application/octet-stream) with:
|
||||
- Writes install.ps1, uninstall.ps1, detect.ps1 into a `TemporaryDirectory`
|
||||
- Extracts driver ZIP into `tmpdir/drivers/`
|
||||
- Calls `build_intunewin(tmpdir, "install.ps1", output_path)` — no subprocess calls
|
||||
- Reads bytes and returns as binary Response
|
||||
|
||||
Both endpoints share `_get_printer_and_driver()` helper (404 for missing printer, 422 for no/invalid driver) and validate the driver ZIP file exists on disk (422 if missing).
|
||||
|
||||
Router registered in `imptune/main.py` after `scripts.router`.
|
||||
|
||||
## Tests
|
||||
|
||||
9 new tests in `tests/test_packages.py`:
|
||||
|
||||
- `TestNinjaDownload`: 5 tests (zip response, install.ps1 in zip, driver files in zip, 404, 422)
|
||||
- `TestIntunewinDownload`: 4 tests (intunewin response, valid outer ZIP structure, 404, 422)
|
||||
|
||||
Full suite result: 84 passed (excluding pre-existing icon upload failures in test_icon_upload.py which existed before this plan).
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Pre-existing Issues (Out of Scope)
|
||||
|
||||
`tests/test_icon_upload.py` has 5 failing tests (`/printers/{id}/icon` returns 404). These failures existed before this plan was executed and are unrelated to package export. Logged for future attention.
|
||||
|
||||
## Commits
|
||||
|
||||
| Hash | Type | Description |
|
||||
| ------- | ------ | ------------------------------------------------------------- |
|
||||
| a31c71e | test | add failing tests for NinjaRMM ZIP and intunewin endpoints |
|
||||
| dd6cedf | feat | implement NinjaRMM ZIP and intunewin package export endpoints |
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND: imptune/api/packages.py
|
||||
- FOUND: tests/test_packages.py
|
||||
- FOUND: imptune/main.py (modified)
|
||||
- FOUND commit a31c71e (RED tests)
|
||||
- FOUND commit dd6cedf (GREEN implementation)
|
||||
Reference in New Issue
Block a user