Skip to content

feat: Rust CLI + library port of the flash pipeline#1

Merged
franklinselva merged 5 commits into
mainfrom
feat/rust-cli-port
Jul 1, 2026
Merged

feat: Rust CLI + library port of the flash pipeline#1
franklinselva merged 5 commits into
mainfrom
feat/rust-cli-port

Conversation

@franklinselva

Copy link
Copy Markdown
Collaborator

Ports the just/bash flashing pipeline to a Rust crate (lib jetson_flash + bin jetson-flash), kept alongside the existing scripts. Same six stages: deps, fetch, stage, preconfig, check, flash (+ all).

What

  • Config: TOML (jetson-flash.toml) via figment; JETSON_* env vars override.
  • Recovery detection: native libusb (rusb) — no lsusb parsing. Exposes typed UsbState / Model for library consumers.
  • Native logic: NetworkManager keyfile + identity baking, UUIDs, config parsing.
  • Still subprocess: NVIDIA l4t_*.sh / apply_binaries.sh and apt/wget/tar, driven through a logged runner.
  • Typed errors: thiserror enum (Error, RecoveryError) instead of anyhow strings; anyhow stays only in the binary.
  • Terminal UX: subprocess output captured to logs/<step>-<ts>.log with an indicatif spinner (console stays clean, unlike bash). -v/--verbose streams live. Downloads (wget) and the flash use live stdio so their native progress shows. On failure the captured log tail is printed.

Layout

bash / just Rust CLI
just <step> jetson-flash <step>
.env jetson-flash.toml
lsusb parse native libusb (rusb)

Library use

Each stage is fn(&Config, &Paths, &Logger) -> Result<()>, callable from other Rust commissioning tooling.

Notes

  • Env override splits on _, so leaf keys containing _ (external_device, static_ip, bsp_url, rootfs_url) are TOML-only; single-word leaves (board.name, identity.hostname) work.
  • rusb vendors libusb1-sys (cc build) — no system libusb-dev needed.
  • Not yet flash-validated on hardware; builds clean (debug + release), clippy clean, check verified against live USB.

Port the just/bash pipeline to a Rust crate (lib `jetson_flash` + bin
`jetson-flash`), kept alongside the existing scripts. Same six stages:
deps, fetch, stage, preconfig, check, flash.

- Config: TOML (`jetson-flash.toml`) via figment, `JETSON_*` env override.
- Recovery detection: native libusb (rusb), no lsusb parsing; exposes a
  typed `UsbState`/`Model` for library consumers.
- NM keyfile + identity baking done natively; NVIDIA `l4t_*.sh` /
  `apply_binaries.sh` and apt/wget/tar stay as subprocesses.
- Typed error enum (thiserror) instead of anyhow strings; anyhow only in
  the binary.
- Clean terminal UX: subprocess output captured to `logs/<step>-<ts>.log`
  with an indicatif spinner; `-v/--verbose` streams live. Downloads and
  the flash use live stdio so their native progress shows. On failure the
  captured log tail is printed.
- stage: explicit skip logs when Linux_for_Tegra/, rootfs, or applied
  NVIDIA binaries are already present (reuse instead of silent skip).
- preconfig: skip l4t_create_default_user.sh (not idempotent) when the
  user is already baked into rootfs/etc/passwd, so preconfig is safe to
  re-run.
- Cargo.toml: repository/readme/keywords/categories/rust-version + publish
  exclude (work, logs, docs, scripts, justfile).
- CI workflow: fmt --check, clippy -D warnings, build, test (installs libusb
  for the rusb link dep).
- Release workflow: on a v* tag, verify tag==version, dry-run, then
  `cargo publish` via CARGO_REGISTRY_TOKEN secret — so `cargo install
  jetson-flash` works once tagged.
- CHANGELOG.md (Keep a Changelog) with the 0.1.0 entry.
- README: cargo install instructions.
- rustfmt normalization of stage/preconfig.
- Config is now nested-TOML profiles: a [default] base + one [<name>] table
  per board (orin-nano, orin-agx). Select with --profile / JETSON_PROFILE
  (required for every stage). `Config::load(path, profile)`.
- Secrets removed from the file: identity.password is Option, supplied via
  JETSON_IDENTITY_PASSWORD; wifi psk via JETSON_NETWORK_WIFI_PSK.
- `init` seeds a jetson-flash.toml from a template embedded in the binary
  (include_str!) so an installed CLI self-seeds; destination is
  --config <path>, else --global (XDG), else ./.
- `edit` opens the resolved config in $EDITOR; `profiles` lists profiles.
- Config discovery: --config -> ./jetson-flash.toml -> XDG.
- clap `env` feature enabled (JETSON_PROFILE); friendly errors for unknown/
  missing profile and missing secret.
- README + CHANGELOG updated.
…first docs

- Version 1.0.0 (first release).
- jetpack presets per profile ("6.2.1" -> L4T r36.4.4, "7.2" -> r39.2);
  resolves L4T version + BSP/rootfs URLs; explicit [profile.l4t] overrides.
- Namespaced workspace/cache: downloads/<ver>/ (shared) + work|logs/<profile>-<ver>/
  so boards and JetPack versions never collide. Base = --work-dir, else repo
  checkout, else ~/.cache/jetson-flash.
- Library pipeline API: Step, run_step, run_all (open per-slot log + dispatch);
  binary reuses them (dropped the bespoke Runner). examples/pipeline.rs added.
- Bare `jetson-flash` prints help (exit 0) instead of a usage error.
- Fix: NM dns keyfile double-semicolon.
- Docs: Rust CLI is now primary; just/bash moved to docs/legacy-just.md.
  Both Orin Nano Super and AGX Orin marked hardware-validated (JetPack 7.2).
  Prereqs trimmed to cargo + libusb; sudo-TTY + recovery-ID troubleshooting.
- Validated end-to-end on hardware: fetch/stage/preconfig/check/flash of an
  Orin Nano Super, NVMe + QSPI, "Flash is successful".
@franklinselva franklinselva merged commit 42251b4 into main Jul 1, 2026
1 check passed
@franklinselva franklinselva deleted the feat/rust-cli-port branch July 1, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant