feat(web,docs): Linux one-click in the card — opt-in hint, staged and nothing-newer outcomes
audit / bun-audit (sdk) (push) Successful in 18s
audit / bun-audit (web) (push) Successful in 18s
audit / docs-site-audit (push) Successful in 23s
audit / pnpm-audit (push) Successful in 12s
audit / cargo-audit (push) Successful in 37s
audit / bun-audit (plugin-kit) (push) Successful in 1m14s
arch / build-publish (push) Failing after 1m17s
ci / rust-arm64 (push) Failing after 44s
ci / web (push) Successful in 1m5s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 21s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 6s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 5s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 4s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7s
ci / docs-site (push) Successful in 2m18s
deb / build-publish-host (push) Failing after 2m15s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 53s
deb / build-publish (push) Failing after 3m43s
audit / license-gate (push) Successful in 4m47s
deb / build-publish-client-arm64 (push) Successful in 3m52s
docker / builders-arm64cross (push) Successful in 14s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 2m49s
ci / rust (push) Failing after 3m6s
docker / deploy-docs (push) Successful in 55s
windows-host / package (push) Failing after 6m27s
windows-host / canary-manifest (push) Skipped
windows-host / winget-source (push) Skipped
android / android (push) Successful in 8m21s
apple / swift (push) Canceled after 0s
apple / screenshots (push) Canceled after 0s
flatpak / build-publish (push) Successful in 4m34s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 4m19s
release / apple (push) Canceled after 57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 4m45s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Canceled after 2m26s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Canceled after 0s
windows / build (aarch64-pc-windows-msvc) (push) Canceled after 0s
windows / build (x86_64-pc-windows-msvc) (push) Canceled after 0s
audit / bun-audit (sdk) (push) Successful in 18s
audit / bun-audit (web) (push) Successful in 18s
audit / docs-site-audit (push) Successful in 23s
audit / pnpm-audit (push) Successful in 12s
audit / cargo-audit (push) Successful in 37s
audit / bun-audit (plugin-kit) (push) Successful in 1m14s
arch / build-publish (push) Failing after 1m17s
ci / rust-arm64 (push) Failing after 44s
ci / web (push) Successful in 1m5s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 21s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 6s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 5s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 4s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7s
ci / docs-site (push) Successful in 2m18s
deb / build-publish-host (push) Failing after 2m15s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 53s
deb / build-publish (push) Failing after 3m43s
audit / license-gate (push) Successful in 4m47s
deb / build-publish-client-arm64 (push) Successful in 3m52s
docker / builders-arm64cross (push) Successful in 14s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 2m49s
ci / rust (push) Failing after 3m6s
docker / deploy-docs (push) Successful in 55s
windows-host / package (push) Failing after 6m27s
windows-host / canary-manifest (push) Skipped
windows-host / winget-source (push) Skipped
android / android (push) Successful in 8m21s
apple / swift (push) Canceled after 0s
apple / screenshots (push) Canceled after 0s
flatpak / build-publish (push) Successful in 4m34s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 4m19s
release / apple (push) Canceled after 57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 4m45s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Canceled after 2m26s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Canceled after 0s
windows / build (aarch64-pc-windows-msvc) (push) Canceled after 0s
windows / build (x86_64-pc-windows-msvc) (push) Canceled after 0s
The apply panel serves staged kinds too; notify mode shows the group-join command when the helper is installed but not yet enabled; outcomes render 'staged — reboot to finish' and 'your package source had nothing newer yet' distinctly. Docs: the Linux opt-in section (why it's a group, what the grant bounds, the pacman stance). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -50,8 +50,27 @@ Every attempt leaves a result in the card (and an installer log under
|
||||
failed update is never silent. To disable the button entirely on a host, set
|
||||
`PUNKTFUNK_UPDATE_APPLY=0` in `host.env`; the card then shows the manual command instead.
|
||||
|
||||
One-click updating for the Linux install methods is on the way — it will be opt-in per host
|
||||
(joining a `punktfunk-update` group) because it needs a narrowly-scoped root helper.
|
||||
## One-click updating (Linux — opt-in)
|
||||
|
||||
The apt, dnf, Bazzite-sysext, and rpm-ostree installs can one-click update too, via a small
|
||||
root helper the packages ship (`pf-update` + a `punktfunk-update.service` oneshot). It's **off
|
||||
until you opt in**, because a web button that ends in root deserves an explicit decision:
|
||||
|
||||
```bash
|
||||
sudo usermod -aG punktfunk-update $USER # then log out and back in
|
||||
```
|
||||
|
||||
That group membership is the entire grant — a polkit rule lets its members start exactly that
|
||||
one service, whose only job is "run this system's normal package update for the punktfunk
|
||||
packages, then prove the new binary runs". The button never chooses versions or URLs; your
|
||||
package manager's own signed repositories stay the source of truth. The card shows the opt-in
|
||||
command until you've done this, and the manual command always keeps working.
|
||||
|
||||
Notes per method: on **rpm-ostree** the update is staged and the card will say so — reboot to
|
||||
finish (the console never reboots your machine). On **Arch/pacman** the button additionally
|
||||
requires `PACMAN_FULL_SYSUPGRADE=1` in `/etc/punktfunk/update.conf`, because the only safe
|
||||
pacman update is a full `pacman -Syu` — partial upgrades are how Arch boxes break, and we
|
||||
won't run one. After a successful update the host restarts itself and the page reconnects.
|
||||
|
||||
## Turning the check off
|
||||
|
||||
|
||||
@@ -476,6 +476,9 @@
|
||||
"update_stage_restarting": "Der Host startet neu…",
|
||||
"update_stage_reconnecting": "Warte, bis der Host wieder da ist…",
|
||||
"update_result_ok": "Aktualisiert {from} → {to}.",
|
||||
"update_result_staged": "Update auf {to} ist vorbereitet — zum Abschließen den Host neu starten.",
|
||||
"update_result_noop": "Deine Paketquelle hatte noch nichts Neueres — in ein paar Minuten erneut versuchen.",
|
||||
"update_opt_in": "Um Ein-Klick-Updates von hier zu aktivieren, einmal auf dem Host ausführen (danach ab- und wieder anmelden):",
|
||||
"update_result_failed": "Update auf {to} ist in Phase {stage} fehlgeschlagen.",
|
||||
"update_result_log": "Installer-Log:"
|
||||
}
|
||||
|
||||
@@ -476,6 +476,9 @@
|
||||
"update_stage_restarting": "The host is restarting…",
|
||||
"update_stage_reconnecting": "Waiting for the host to come back…",
|
||||
"update_result_ok": "Updated {from} → {to}.",
|
||||
"update_result_staged": "Update to {to} is staged — reboot this host to finish.",
|
||||
"update_result_noop": "Your package source had nothing newer yet — try again in a few minutes.",
|
||||
"update_opt_in": "To enable one-click updates from here, run this once on the host (then log out and back in):",
|
||||
"update_result_failed": "Update to {to} failed during {stage}.",
|
||||
"update_result_log": "Installer log:"
|
||||
}
|
||||
|
||||
@@ -48,14 +48,15 @@ export const UpdateSection: FC = () => {
|
||||
const check = useForceUpdateCheck();
|
||||
|
||||
const s = status.data;
|
||||
// The apply resolved: the host answers with the target version (success — reconcile wrote
|
||||
// last_result), or reports a failed attempt at our target.
|
||||
// The apply resolved: either the host answers with the target version (reconcile wrote
|
||||
// last_result across the restart), or ANY apply outcome newer than our start arrived —
|
||||
// which covers failure, "staged, reboot to finish", and "your package source had nothing
|
||||
// newer yet" (where the version deliberately doesn't change).
|
||||
if (applying && s) {
|
||||
if (
|
||||
s.current_version === applying.target ||
|
||||
(s.last_result &&
|
||||
s.last_result.to === applying.target &&
|
||||
!s.last_result.ok)
|
||||
s.last_result.finished_unix * 1000 >= applying.startedAt - 60_000)
|
||||
) {
|
||||
setApplying(null);
|
||||
}
|
||||
@@ -157,12 +158,20 @@ export const UpdateCard: FC<{
|
||||
timedOut={timedOut}
|
||||
/>
|
||||
) : s.available ? (
|
||||
s.apply === "full" ? (
|
||||
s.apply === "full" || s.apply === "staged" ? (
|
||||
<ApplyPanel status={s} onApplied={onApplied} />
|
||||
) : (
|
||||
<div className="space-y-2 rounded-md border p-4">
|
||||
<p className="text-sm">{m.update_how()}</p>
|
||||
<CommandLine command={s.channel_hint} />
|
||||
{s.opt_in_hint && (
|
||||
<div className="space-y-1 border-t pt-2">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{m.update_opt_in()}
|
||||
</p>
|
||||
<CommandLine command={s.opt_in_hint} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
@@ -402,7 +411,11 @@ const LastResult: FC<{
|
||||
}> = ({ result }) =>
|
||||
result.ok ? (
|
||||
<p className="rounded-md border border-emerald-500/40 bg-emerald-500/10 p-3 text-sm">
|
||||
{m.update_result_ok({ from: result.from, to: result.to })}
|
||||
{result.staged
|
||||
? m.update_result_staged({ to: result.to })
|
||||
: result.from === result.to
|
||||
? m.update_result_noop()
|
||||
: m.update_result_ok({ from: result.from, to: result.to })}
|
||||
</p>
|
||||
) : (
|
||||
<div className="space-y-1 rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm">
|
||||
|
||||
Reference in New Issue
Block a user