YouDuck.ai

Build here, install on the board

Last updated

The loop between changing a line and watching the robot run it, with no push, no CI run and no tag. About a minute on an incremental build, against several for a push plus a CI run.

scripts/dev-push.sh is that command.

Once, before the first push

The board has to be a dev board. The artifact is signed with the team dev key, so a customer robot refuses it exactly as it refuses --ref. See Setting up a dev board.

The dev signing key goes at ~/.duck-keys/team.dev.key. Set DUCK_DEV_SECRET_KEY if yours lives elsewhere.

A toolchain that can build for the board. Either install the cross-compiler:

cargo install cargo-zigbuild --locked
brew install zig

Or install nothing and pass --docker, which needs only a running Docker daemon — slower to start, and the path to reach for before you have a board at all.

The loop

scripts/dev-push.sh --name duck-c51b
# or once per shell
export DUCK_ROBOT=duck-c51b
scripts/dev-push.sh

The name is the one duckctl scan lists. Its address is asked for over Bluetooth and then cached, so only a push that cannot reach the cached address goes back to the radio. That is what makes a new DHCP lease, a reflash or a different network cost nothing to follow.

The ssh user is radxa; export DUCK_BOARD_USER=pierre if yours is not. An address works too and skips the radio entirely.

What it does

Cross-compiles the workspace, packages the same artifact a release does, signs it with the dev key, copies it to ~/duck-sideload on the board, and applies it there through robotctl update apply --from. Then it waits for the daemons to report the new release:

==> building 0.5.1-dev.local.1763400000.g7fc1444 for the board (zigbuild)
==> signing with /Users/you/.duck-keys/team.dev.key
==> applying on radxa@192.168.1.42
==> checking every daemon is running it
    [ok] robotd
    [ok] configd
    ...

Which “problems” in that output are not problems

This saves a lot of misreading.

updaterd and btd restart five seconds after the apply replies, so those two lines take a moment. Not a hang.

[--] padd published nothing is not a failure. It is also what a stopped or deliberately disabled daemon looks like, and what mediad looks like on a board with no camera.

[--] tofd published nothing means a release from before tofd published an identity at all — one more push fixes it — rather than a sensor that is missing. tofd runs whether or not one is fitted.

It is an ordinary update

The signature, the artifact hash, compatibility, the health gate and auto-rollback all run. A build that does not come up is reverted and the board is back on what it was running. Going back on purpose is the ordinary command too:

sudo robotctl update rollback daemon

Two pushes of the same dirty tree never collide — the version carries the push’s timestamp, not just the commit. The tree is expected to be dirty here.

Watching what you pushed

From a second terminal, before the push, so the restart shows up in it:

ssh radxa@192.168.1.42 'journalctl -f -u robotd -u configd -u btd -u padd'

-u updaterd on its own is the update itself — each phase, the health gate, and the restarts it triggers.