CI: build, sign and publish firmware releases on Gitea #5

Closed
opened 2026-10-05 09:37:58 +00:00 by twisla · 1 comment
Owner

Idea

Pushing a tag builds the release firmware and the Debug Build in CI, makes signed Update Files (.ota) and creates a Gitea release with the files and the notes attached.

Why

Today releases are built and signed on my machine. To install releases from the device, the images have to be built the same way every time and published where the device can find them.

What's known

  • Gitea Actions is turned off for the repo and no runner is registered, so both need setting up. A runner using Docker can reuse scripts/ci.sh and the same build image.
  • Signing is the hard part.
    • The private key is in ~/.config/roro9stack/ota-key.pem. Putting it in CI as a secret means a CI compromise can sign firmware every device would accept.
    • Options:
      • Accept that risk.
      • Have CI build, then sign locally and upload the signature.
      • Give CI its own key, which the firmware would also have to trust.
  • Reproducible builds: the same tag should give the same image. This also helps check that CI built what the tag says.
  • Assets per release:
    • firmware.ota, and a +debug.ota for the Debug Build.
    • factory.bin for USB flashing.
    • The ELF files, so crash backtraces can be decoded. Today those are archived in .pio/elves.
    • Checksums.

Questions for the design round

  1. Which runner: on the dev box, or somewhere else? Which architecture? Docker?
  2. Where does signing happen: in CI with a secret, locally afterwards, or with a separate CI key?
  3. Should every tag make a release, or only tags matching v*? Should there be pre-releases for testing?
  4. What goes in the release notes: the tag message, or a summary of the milestone?
  5. Should the ELF files be published, or kept private?
## Idea Pushing a tag builds the release firmware and the Debug Build in CI, makes signed Update Files (`.ota`) and creates a Gitea release with the files and the notes attached. ## Why Today releases are built and signed on my machine. To install releases from the device, the images have to be built the same way every time and published where the device can find them. ## What's known - **Gitea Actions** is turned off for the repo and no runner is registered, so both need setting up. A runner using Docker can reuse `scripts/ci.sh` and the same build image. - **Signing is the hard part.** - The private key is in `~/.config/roro9stack/ota-key.pem`. Putting it in CI as a secret means a CI compromise can sign firmware every device would accept. - Options: - Accept that risk. - Have CI build, then sign locally and upload the signature. - Give CI its own key, which the firmware would also have to trust. - **Reproducible builds:** the same tag should give the same image. This also helps check that CI built what the tag says. - **Assets per release:** - `firmware.ota`, and a `+debug.ota` for the Debug Build. - `factory.bin` for USB flashing. - The ELF files, so crash backtraces can be decoded. Today those are archived in `.pio/elves`. - Checksums. ## Questions for the design round 1. Which runner: on the dev box, or somewhere else? Which architecture? Docker? 2. Where does signing happen: in CI with a secret, locally afterwards, or with a separate CI key? 3. Should every tag make a release, or only tags matching `v*`? Should there be pre-releases for testing? 4. What goes in the release notes: the tag message, or a summary of the milestone? 5. Should the ELF files be published, or kept private?
twisla added a new dependency 2026-10-05 09:37:58 +00:00
twisla added this to the R1 Releases milestone 2026-10-05 20:14:08 +00:00
Author
Owner

In place (merge edc140e). .gitea/workflows/ci.yml runs the host tests and both builds on every push; a tag v* also builds, signs and publishes a release. The thirteen tags from v0.1.0 to v0.10.0 have their releases, and each Update File was downloaded and verified. Decisions and how it was built: docs/milestones/R1.md, docs/adr/0008-ci-signs-releases.md.

In place (merge edc140e). `.gitea/workflows/ci.yml` runs the host tests and both builds on every push; a tag `v*` also builds, signs and publishes a release. The thirteen tags from v0.1.0 to v0.10.0 have their releases, and each Update File was downloaded and verified. Decisions and how it was built: `docs/milestones/R1.md`, `docs/adr/0008-ci-signs-releases.md`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: twisla/roro9stack#5