Files
android-fs5/specs/001-android-fs5-management/plan.md
T
Jane Alesi e6e5f34455 feat: add android-fs5 CLI toolset
- fs5 entrypoint with status, push, pull, app, shell, logs commands
- lib/adb.sh: ADB wrapper functions with idempotency and --force guards
- lib/output.sh: human + JSON output formatting via Python
- 33 bats tests (6 status, 9 transfer, 7 app, 5 shell, 6 logs) — all passing
- Zero shellcheck warnings
- .env-based config (device serial, log path)
- Spec-Driven Development artifacts in specs/001-android-fs5-management/

Device: exone GmbH FS5 (RD51QE202392, Android 9)
2026-03-02 15:55:09 +01:00

205 lines
5.8 KiB
Markdown

# Implementation Plan: Android FS5 Device Management
**Feature**: 001-android-fs5-management
**Created**: 2026-03-02
**Tech Stack**: Bash 5, ADB 1.0.41, Python 3 (for JSON output), ShellCheck
---
## Constitution Check
- [x] **Article I (Shell-First)**: Implemented as Bash CLI with Python for JSON formatting.
- [x] **Article II (ADB Interface)**: All device ops go through `adb -s $DEVICE_SERIAL`.
- [x] **Article III (Test-First)**: Tests written in `tests/` using bats-core before implementation.
- [x] **Article IV (Idempotency)**: All operations check state before mutating.
- [x] **Article V (Non-Destructive)**: Destructive ops require `--force`; all writes logged.
- [x] **Article VI (Device Identity)**: Serial loaded from `.env`, validated on every run.
- [x] **Article VII (Simplicity)**: 3 modules: `fs5` (entrypoint), `lib/adb.sh` (ADB helpers), `lib/output.sh` (formatting).
- [x] **Article VIII (Structured Output)**: `--json` flag on all commands via Python `json` module.
- [x] **Article IX (Config over Hardcoding)**: `.env` file with `DEVICE_SERIAL`, `LOG_FILE`, `DEFAULT_PUSH_PATH`.
---
## Architecture
```text
android-fs5/
├── fs5 # Main entrypoint (Bash, chmod +x)
├── lib/
│ ├── adb.sh # ADB wrapper functions
│ └── output.sh # Human/JSON output formatting
├── tests/
│ ├── test_status.bats # bats-core tests for status command
│ ├── test_transfer.bats # bats-core tests for push/pull
│ ├── test_app.bats # bats-core tests for app management
│ ├── test_shell.bats # bats-core tests for shell command
│ └── test_logs.bats # bats-core tests for log capture
├── .env.example # Template config (committed)
├── .env # Local config (gitignored)
├── .gitignore
├── README.md
└── fs5.log # Operation log (gitignored)
```
---
## Module Design
### `fs5` — Main Entrypoint
```text
Usage: ./fs5 <command> [options]
Commands:
status Show device health and info
push <src> <dst> Push file/dir to device
pull <src> <dst> Pull file/dir from device
app list List installed apps
app install <apk> Install APK
app uninstall <pkg> Uninstall package
shell [cmd] Execute shell command on device
logs Stream logcat output
Global Options:
--json Output as JSON
--force Allow destructive operations
--help Show help
```
### `lib/adb.sh` — ADB Helpers
Key functions:
- `adb_check_device()` — validates device is connected, exits 2 if not
- `adb_cmd()` — wraps `adb -s $DEVICE_SERIAL` with error handling
- `adb_get_prop(key)` — reads device property
- `adb_shell(cmd)` — executes shell command on device
- `adb_push(src, dst)` — pushes file with existence check
- `adb_pull(src, dst)` — pulls file
- `adb_log_op(op, detail)` — appends timestamped entry to `fs5.log`
### `lib/output.sh` — Output Formatting
Key functions:
- `out_human(msg)` — prints to stdout
- `out_json(data)` — formats key=value pairs as JSON via Python
- `out_error(msg)` — prints to stderr with red color
- `out_success(msg)` — prints with green checkmark
---
## Data Model
### Config (`.env`)
```bash
DEVICE_SERIAL=RD51QE202392
LOG_FILE=./fs5.log
DEFAULT_PUSH_PATH=/sdcard/
ADB_TIMEOUT=10
```
### Device Status Object (JSON)
```json
{
"serial": "RD51QE202392",
"model": "FS5",
"manufacturer": "exone GmbH",
"android_version": "9",
"battery_level": 85,
"battery_charging": true,
"storage_total_kb": 54302616,
"storage_used_kb": 18228852,
"storage_free_kb": 35926308,
"storage_percent_used": 34,
"adb_state": "device",
"timestamp": "2026-03-02T15:43:00+01:00"
}
```
### Log Entry Format
```text
2026-03-02T15:43:00+01:00 PUSH /home/ja/file.txt -> /sdcard/file.txt [OK]
2026-03-02T15:44:00+01:00 INSTALL /tmp/app.apk -> com.example.app [OK]
```
---
## Technical Decisions
### Why Bash over Python?
- ADB is a CLI tool — Bash is the natural glue language.
- No dependency installation required on the host.
- Python used only for JSON serialization (available on all Manjaro systems).
### Why bats-core for testing?
- Purpose-built for Bash script testing.
- Supports mocking ADB calls for offline testing.
- Integrates with CI/CD pipelines.
### JSON Output Strategy
Use Python one-liner to convert shell variables to JSON:
```bash
python3 -c "import json,sys; d={...}; print(json.dumps(d, indent=2))"
```
This avoids `jq` as a hard dependency while producing valid JSON.
### Exit Code Strategy
| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | General error (bad args, operation failed) |
| 2 | Device not found / ADB error |
---
## Dependencies
| Tool | Version | Required | Install |
|------|---------|----------|---------|
| adb | 1.0.41+ | YES | `sudo pacman -S android-tools` |
| bash | 5.x | YES | Pre-installed |
| python3 | 3.x | YES | Pre-installed |
| bats-core | 1.x | DEV | `sudo pacman -S bash-bats` |
| shellcheck | 0.9+ | DEV | `sudo pacman -S shellcheck` |
---
## Validation Scenarios (Quickstart)
```bash
# 1. Verify device connected
./fs5 status
# 2. Test JSON output
./fs5 status --json | python3 -m json.tool
# 3. Test file transfer
echo "test" > /tmp/fs5-test.txt
./fs5 push /tmp/fs5-test.txt /sdcard/fs5-test.txt
./fs5 pull /sdcard/fs5-test.txt /tmp/fs5-pulled.txt
diff /tmp/fs5-test.txt /tmp/fs5-pulled.txt && echo "PASS"
# 4. Test idempotency
./fs5 push /tmp/fs5-test.txt /sdcard/fs5-test.txt # Should warn, not fail
# 5. Test app listing
./fs5 app list
# 6. Test shell passthrough
./fs5 shell "echo hello from device"
# 7. Test log capture
./fs5 logs --lines 5
# 8. Test disconnected behavior (unplug device first)
./fs5 status; echo "Exit code: $?" # Should be 2
```