Skip to content

Commit 5fce6d1

Browse files
committed
feat(firmware): add target-safe updates
Store firmware releases in the default tenant and filter kiosk pins and rollouts by detected firmware target or OS compatibility.
1 parent 618721d commit 5fce6d1

21 files changed

Lines changed: 473 additions & 106 deletions

File tree

.github/workflows/build.yml

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,13 @@ jobs:
145145
fail-fast: false
146146
matrix:
147147
include:
148-
- target: aarch64-unknown-linux-gnu
148+
- rust_target: aarch64-unknown-linux-gnu
149+
firmware_target: betterframe-rpi5-aarch64
150+
label: Raspberry Pi 5
149151
runs-on: blacksmith-8vcpu-ubuntu-2404-arm
150-
- target: x86_64-unknown-linux-gnu
152+
- rust_target: x86_64-unknown-linux-gnu
153+
firmware_target: betterframe-pc-x86_64
154+
label: PC x86_64
151155
runs-on: blacksmith-8vcpu-ubuntu-2404
152156
runs-on: ${{ matrix.runs-on }}
153157
# Trixie container matches Pi OS Trixie's glibc + apt packages.
@@ -174,29 +178,30 @@ jobs:
174178
- name: Install Rust toolchain
175179
uses: dtolnay/rust-toolchain@stable
176180
with:
177-
targets: ${{ matrix.target }}
181+
targets: ${{ matrix.rust_target }}
178182

179183
- name: cargo build --release
180184
working-directory: kiosk
181185
env:
182-
BF_BUILD_ARCH: ${{ matrix.target }}
186+
BF_BUILD_ARCH: ${{ matrix.rust_target }}
187+
BF_FIRMWARE_TARGET: ${{ matrix.firmware_target }}
183188
BF_BUILD_VERSION: ${{ inputs.version }}
184189
BF_AXIOM_KEY: ${{ secrets.BF_AXIOM_KEY }}
185190
BF_AXIOM_DATASET: ${{ secrets.BF_AXIOM_DATASET }}
186-
run: cargo build --release --target ${{ matrix.target }}
191+
run: cargo build --release --target ${{ matrix.rust_target }}
187192

188193
- name: Strip + rename
189194
working-directory: kiosk
190195
run: |
191-
strip target/${{ matrix.target }}/release/betterframe-kiosk
192-
cp target/${{ matrix.target }}/release/betterframe-kiosk \
193-
betterframe-kiosk-${{ inputs.version }}-${{ matrix.target }}
196+
strip target/${{ matrix.rust_target }}/release/betterframe-kiosk
197+
cp target/${{ matrix.rust_target }}/release/betterframe-kiosk \
198+
betterframe-kiosk-${{ inputs.version }}-${{ matrix.firmware_target }}
194199
195200
- name: Upload to GitHub Release (binary)
196201
uses: softprops/action-gh-release@v3
197202
with:
198203
tag_name: ${{ inputs.tag }}
199-
files: kiosk/betterframe-kiosk-${{ inputs.version }}-${{ matrix.target }}
204+
files: kiosk/betterframe-kiosk-${{ inputs.version }}-${{ matrix.firmware_target }}
200205

201206
- name: Auto-import into BF server
202207
if: env.BF_AUTOIMPORT_URL != '' && env.BF_AUTOIMPORT_API_KEY != ''
@@ -205,15 +210,15 @@ jobs:
205210
BF_AUTOIMPORT_API_KEY: ${{ secrets.BF_AUTOIMPORT_API_KEY }}
206211
working-directory: kiosk
207212
run: |
208-
bin="betterframe-kiosk-${{ inputs.version }}-${{ matrix.target }}"
213+
bin="betterframe-kiosk-${{ inputs.version }}-${{ matrix.firmware_target }}"
209214
base64 -w 0 "$bin" > "${bin}.b64"
210215
jq -nc \
211216
--arg v "${{ inputs.version }}" \
212217
--arg c "${{ inputs.channel }}" \
213-
--arg a "${{ matrix.target }}" \
214-
--arg n "GitHub Actions build of ${{ inputs.tag }} (${{ github.sha }})" \
218+
--arg t "${{ matrix.firmware_target }}" \
219+
--arg n "GitHub Actions ${{ matrix.label }} build of ${{ inputs.tag }} (${{ github.sha }})" \
215220
--rawfile b "${bin}.b64" \
216-
'{version:$v, channel:$c, arch:$a, release_notes:$n, content_b64:$b}' \
221+
'{version:$v, channel:$c, target:$t, release_notes:$n, content_b64:$b}' \
217222
> "${bin}.import.json"
218223
for attempt in 1 2 3; do
219224
if curl -sSf --retry 2 --retry-delay 5 -X POST \
@@ -230,8 +235,8 @@ jobs:
230235
- name: Upload artifact (always)
231236
uses: actions/upload-artifact@v7
232237
with:
233-
name: betterframe-kiosk-${{ matrix.target }}
234-
path: kiosk/betterframe-kiosk-${{ inputs.version }}-${{ matrix.target }}
238+
name: betterframe-kiosk-${{ matrix.firmware_target }}
239+
path: kiosk/betterframe-kiosk-${{ inputs.version }}-${{ matrix.firmware_target }}
235240
retention-days: 14
236241

237242
# ---- Flashable Pi OS Trixie image (aarch64 only) -------------------------
@@ -258,14 +263,14 @@ jobs:
258263
- name: Pull kiosk aarch64 binary from this run's artifact
259264
uses: actions/download-artifact@v8
260265
with:
261-
name: betterframe-kiosk-aarch64-unknown-linux-gnu
266+
name: betterframe-kiosk-betterframe-rpi5-aarch64
262267
path: staging/
263268

264269
- name: Render BF logo for plymouth
265270
run: |
266271
sudo apt-get -y update
267272
sudo apt-get -y install --no-install-recommends librsvg2-bin
268-
mv staging/betterframe-kiosk-${{ inputs.version }}-aarch64-unknown-linux-gnu \
273+
mv staging/betterframe-kiosk-${{ inputs.version }}-betterframe-rpi5-aarch64 \
269274
staging/betterframe-kiosk
270275
chmod +x staging/betterframe-kiosk
271276
rsvg-convert -w 480 server/src/web-static/betterframe-logo.svg -o staging/logo.png

kiosk/src/firmware.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ pub const ARCH: &str = match option_env!("BF_BUILD_ARCH") {
3737
None => "aarch64-unknown-linux-gnu",
3838
};
3939

40+
pub const FIRMWARE_TARGET: &str = match option_env!("BF_FIRMWARE_TARGET") {
41+
Some(s) => s,
42+
None => match option_env!("BF_BUILD_ARCH") {
43+
Some("aarch64-unknown-linux-gnu") => "betterframe-rpi5-aarch64",
44+
Some("x86_64-unknown-linux-gnu") => "betterframe-pc-x86_64",
45+
Some(s) => s,
46+
None => "betterframe-rpi5-aarch64",
47+
},
48+
};
49+
4050
const DEFAULT_BIN_PATH: &str = "/opt/betterframe/kiosk/betterframe-kiosk";
4151
const FIRMWARE_MARKER: &str = "/var/lib/betterframe/kiosk/firmware-applying.json";
4252
const FIRMWARE_ATTEMPTS: &str = "/var/lib/betterframe/kiosk/firmware-applying.attempts";
@@ -92,7 +102,8 @@ pub struct UpdateInfo {
92102
/// channel. Used before pairing to self-update to latest binary.
93103
pub fn check_public(server: &str, current_version: &str) -> Option<UpdateInfo> {
94104
let url = format!(
95-
"{server}/api/firmware/public/check?arch={arch}&current={cur}",
105+
"{server}/api/firmware/public/check?target={target}&arch={arch}&current={cur}",
106+
target = FIRMWARE_TARGET,
96107
arch = ARCH,
97108
cur = current_version,
98109
);
@@ -165,7 +176,8 @@ pub fn check(server: &str, key: &str, current_version: &str) -> Option<UpdateInf
165176
// current_version is semver-shaped (already URL-safe). Empty string is
166177
// fine — server treats it as "unknown" and offers any release.
167178
let url = format!(
168-
"{server}/api/kiosk/firmware/check?arch={arch}&current={cur}",
179+
"{server}/api/kiosk/firmware/check?target={target}&arch={arch}&current={cur}",
180+
target = FIRMWARE_TARGET,
169181
arch = ARCH,
170182
cur = current_version,
171183
);

kiosk/src/os_update.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ use tracing::{info, warn};
4141
pub const DEFAULT_COMPATIBILITY: &str = "betterframe-rpi5-aarch64";
4242
static CANCEL_REQUESTED: AtomicBool = AtomicBool::new(false);
4343

44+
pub fn compatibility_public() -> String { compatibility() }
45+
4446
fn compatibility() -> String {
4547
if let Ok(s) = fs::read_to_string("/etc/betterframe/os-compatibility") {
4648
let trimmed = s.trim();

kiosk/src/server.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ pub fn initiate_pairing(server: &str) -> (String, String) {
372372
.json(&serde_json::json!({
373373
"proposed_name": hostname,
374374
"hardware_model": hw_model,
375+
"firmware_target": crate::firmware::FIRMWARE_TARGET,
375376
"capabilities": ["rtsp", "gstreamer", "gtk4"]
376377
}))
377378
.send()
@@ -667,7 +668,9 @@ pub fn heartbeat(
667668
let mut payload = serde_json::json!({
668669
"bundle_version": bundle_version,
669670
"kiosk_app_version": kiosk_app_version(),
671+
"firmware_target": crate::firmware::FIRMWARE_TARGET,
670672
"os_version": crate::os_update::current_os_version_public(),
673+
"os_update_compatibility": crate::os_update::compatibility_public(),
671674
"displays": display_info,
672675
"cpu_temp_c": hw.cpu_temp_c,
673676
"cpu_load_percent": hw.cpu_load_percent,

server/src/plugins/service-admin-http/routes-admin.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import {
6060
normalizeUpdateSchedule,
6161
type UpdateSchedule,
6262
} from "../../shared/update-schedule.js";
63+
import { currentTenantSchema, withDefaultTenant } from "../../shared/default-tenant.js";
6364

6465
interface DiscoverAddStream {
6566
profile_name: string;
@@ -2156,8 +2157,10 @@ export function registerAdminRoutes(app: H3, deps: AdminDeps): void {
21562157
});
21572158
}
21582159
const gpioBindings = await deps.repo.listGpioBindings(id);
2159-
const firmwareReleases = await deps.repo.listFirmwareReleases();
2160-
const osReleases = await deps.repo.listOsUpdateReleases();
2160+
const [firmwareReleases, osReleases] = await withDefaultTenant(deps.repo, currentTenantSchema(event), async () => Promise.all([
2161+
deps.repo.listFirmwareReleases(),
2162+
deps.repo.listOsUpdateReleases(),
2163+
]));
21612164
const logResult = await deps.repo.queryKioskLogs({ kiosk_id: id, limit: 50 });
21622165
return htmlPage(KioskEditPage({
21632166
user: user.username,
@@ -2766,7 +2769,12 @@ export function registerAdminRoutes(app: H3, deps: AdminDeps): void {
27662769
const html = `<form method="post" action="/admin/tenants/switch" style="padding:0.5rem 1rem">
27672770
<label style="font-size:0.75rem; color:#888; display:block; margin-bottom:0.25rem">Tenant</label>
27682771
<select name="tenant_slug" style="width:100%; font-size:0.8rem; padding:0.25rem" onchange="this.form.submit()">${options}</select>
2769-
</form>`;
2772+
</form>
2773+
<script>
2774+
document.querySelectorAll('[data-core-only="true"]').forEach(function (el) {
2775+
el.style.display = ${JSON.stringify(current === "default" ? "" : "none")};
2776+
});
2777+
</script>`;
27702778
return new Response(html, { headers: { "content-type": "text/html" } });
27712779
});
27722780

0 commit comments

Comments
 (0)