Skip to content

feat: add fbuild sync and JSON platformio.lock #618

@zackees

Description

@zackees

Context

We need a tracked design/implementation issue for fbuild sync, a uv-like dependency sync command for PlatformIO projects.

fbuild sync should read platformio.ini, resolve dependency identities, install missing packages into the existing fbuild cache, and write a deterministic JSON platformio.lock next to platformio.ini.

This is primarily for testing, auditing, and reproducible dependency installation at first. Strict build/deploy consumption of the lockfile should be deferred until the lockfile path has enough coverage.

PlatformIO does not appear to have a shipped lockfile convention to mirror. Its docs define dependency declarations through lib_deps, including registry, VCS, archive, and local sources, and platformio-core has an open feature request for Cargo/npm-style exact direct/transitive dependency lockfiles with hashes:
platformio/platformio-core#4613

Repo design note:
tasks/fbuild-sync-design.md

Proposal

Add fbuild sync with these semantics:

  • Default fbuild sync resolves all [env:*] entries.
  • If more than one env is selected, warn and prompt before proceeding.
  • fbuild sync -e <env> resolves one environment and does not prompt.
  • --yes accepts all-env sync scope for scripts/non-interactive use.
  • --locked requires a fresh lockfile, installs missing packages from it, and never rewrites it.
  • --check validates freshness/cache state without installing or writing.
  • --dry-run prints planned lock changes and missing installs.
  • --upgrade and --upgrade-package <name> allow controlled repinning.

The lockfile should be JSON, stable, sorted, and written as:

<project-dir>/platformio.lock

Lock everything that affects dependency identity where fbuild can resolve it:

  • Toolchains.
  • Platform/framework packages.
  • Direct and transitive lib_deps.
  • Remote .lnk blobs.
  • GitHub/VCS deps resolved to immutable commit SHAs; tag specs should record both tag and resolved SHA.
  • Remote/user-overridden board package metadata where applicable.

Represent local deps, but do not pretend they are reproducible:

  • symlink://
  • file://
  • relative paths
  • absolute paths

These should be included as local/unlocked entries.

Decisions

  • JSON v1 should duplicate package records under each env rather than hoisting shared package records to a top-level table.
  • --check should skip the multi-env prompt, including in non-interactive mode, because it does not install or write.
  • Registry metadata should include as much as necessary for reproducible installation, auditing, and future lockfile consumption; avoid copying decorative fields with no consumer.

Implementation touch points

  • crates/fbuild-cli/src/cli/args.rs: add Commands::Sync and include it in KNOWN_SUBCOMMANDS.
  • crates/fbuild-cli/src/cli/dispatch.rs: route sync command.
  • crates/fbuild-daemon/src/main.rs: add POST /api/sync.
  • crates/fbuild-daemon/src/handlers/operations/: add sync handler mirroring /api/install-deps.
  • crates/fbuild-build/src/lib.rs: split dependency identity resolution from immediate installation in PlatformSupport.
  • crates/fbuild-packages/src/library/: make registry/GitHub resolution observable without download side effects.
  • crates/fbuild-packages/src/downloader.rs: expose archive SHA256 capture for lockfile entries.
  • crates/fbuild-config/src/ini_parser/: reuse resolved env and lib_deps parsing for dependency-relevant input hashing.

Acceptance criteria

  • fbuild sync writes deterministic JSON platformio.lock next to platformio.ini.
  • Default sync covers all envs and prompts when more than one env is selected.
  • fbuild sync -e <env> syncs only that env and does not prompt.
  • --yes allows non-interactive all-env sync.
  • --check validates all selected envs without prompting, installing, or writing.
  • --locked installs missing packages from an existing fresh lockfile and does not rewrite it.
  • --check fails on stale/missing required lock entries without installing or writing.
  • GitHub branch dependencies are pinned to immutable commit SHAs in the lockfile.
  • GitHub tag dependencies record tag plus resolved commit SHA.
  • Local dependencies are represented as status: "unlocked".
  • Package records are duplicated under each env in JSON v1.
  • Lockfile writes are atomic and serialized by the existing per-project daemon lock.
  • Shared package installs reuse existing per-package install locks.
  • Build/deploy strict consumption is not required for this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions