Skip to content

refactor(deploy): rewrite install.sh in POSIX sh with systemd + OpenRC support#132

Merged
ZingerLittleBee merged 13 commits into
mainfrom
refactor/install-sh-posix-openrc
Jun 1, 2026
Merged

refactor(deploy): rewrite install.sh in POSIX sh with systemd + OpenRC support#132
ZingerLittleBee merged 13 commits into
mainfrom
refactor/install-sh-posix-openrc

Conversation

@ZingerLittleBee
Copy link
Copy Markdown
Owner

Summary

Rewrites deploy/install.sh from a bash script into portable POSIX sh, adding first-class OpenRC support alongside systemd so the installer works on Alpine and other non-systemd distros. Adds a CI lint gate and release checksum verification, plus an end-to-end VPS test runbook.

What changed

  • deploy/install.sh (POSIX rewrite, ~2200 net lines)

    • No bashisms — passes dash -n and shellcheck --shell=dash.
    • Init abstraction (svc_*) with three backends: systemd, OpenRC (supervise-daemon + logrotate), and none (manual-start fallback).
    • Privilege elevation re-execs the whole process under sudo/doas instead of prefixing every command.
    • Preserves all prior behavior: server/agent × binary/docker install matrix, i18n (CN/EN), capability picker, Caddy + Let's Encrypt domain setup, DNS checks, legacy-layout migration, .install-meta tracking, and the serverbee management CLI.
  • CI / release

    • ci.yml: new install-script job runs dash -n + shellcheck --shell=dash --severity=warning.
    • release.yml: emit sha256sums.txt; the installer verifies downloaded binaries against it (older releases without the file warn-and-continue).
  • Docs / tests

    • Design spec + implementation plan under docs/superpowers/.
    • tests/manual/agent-recover-e2e.md e2e runbook.

Review follow-ups (included as separate commits)

A code review of the rewrite surfaced these, each fixed in its own commit:

  • fix(deploy): match sha256sums on the exact filename field (no substring/regex match on the security-verification path)
  • fix(deploy): print (no logs) when service/docker log output is genuinely empty (sed exits 0 on empty input, so the prior || fallback was dead code)
  • fix(deploy): preserve the section separator when toml_set appends a key into an existing section
  • fix(deploy): forward SERVERBEE_*/LANG_CODE env across doas elevation (doas has no sudo -E equivalent)
  • fix(deploy): --purge removes the snap Docker config dir so no empty husk lingers
  • docs(deploy): clarify the OpenRC respawn bound is a weaker guarantee than systemd's RestartPreventExitStatus=78

Testing

  • dash -n deploy/install.sh — clean
  • shellcheck --shell=dash --severity=warning deploy/install.sh — clean (matches the new CI gate)
  • toml_set key-insertion (mid-section and end-of-file), checksum exact-match, and doas env argv assembly verified locally with standalone harnesses.

Full VPS end-to-end (tests/manual/full-deploy-e2e.md) not yet run on this branch.

…C support

- Convert from bash 4+ (assoc arrays, [[ ]], read -rp, echo -e, C-style for,
  process substitution) to POSIX sh that runs under dash and busybox ash.
- i18n and capability tables collapse from declare -A into single case functions.
- k3s-style init abstraction: detect_init sets INIT (openrc|systemd|none);
  svc_* dispatch layer; systemd unit + OpenRC supervise-daemon generators.
- OpenRC service writes output_log/error_log + logrotate (no journald there).
- sha256 verification of downloaded binaries (warn-and-continue for releases
  without sha256sums.txt).
- doas/sudo privilege handling via re-exec when not root.
- apk Caddy branch + init-aware Caddy enable/start for Alpine.
- Verified: dash -n, busybox ash -n, shellcheck --severity=warning all clean.
uninstall --purge relied on a best-effort rmdir of the base directory,
which silently failed whenever a prior non-purge uninstall of the other
component left files behind, leaving /opt/serverbee in place. When purge
is requested and no components remain, remove the base directory
recursively; the non-purge path still only drops already-empty dirs so
kept config and data are preserved.
Add an install-script lint job to ci.yml running dash -n and
shellcheck --shell=dash on deploy/install.sh, so bashisms and POSIX
syntax regressions are caught in CI.

Rename the release checksum asset from checksums.txt to sha256sums.txt:
install.sh fetches the sha256sums.txt name for binary verification, so
the old name meant the checksum step could never find its file and
always fell through to the warn-and-skip path. Also hash only
serverbee-* to avoid self-including the sums file in the glob.
systemd uses RestartPreventExitStatus=78 to stop the agent when
enrollment fails permanently (exit 78), so it does not burn the
registration rate limit in a tight respawn loop. OpenRC's
supervise-daemon has no per-exit-code equivalent, so a bad enrollment
code would respawn-loop forever.

Add respawn_max=5 + respawn_period=300 to the OpenRC agent init script,
mirroring the systemd unit's StartLimitBurst=5 / StartLimitIntervalSec=300:
more than 5 respawns within 300s and supervise-daemon gives up.

Verified end-to-end on a privileged Alpine OpenRC container: a bogus
enrollment code produces exactly 5 'Permanent registration failure'
log lines, then supervise-daemon stops respawning and the service
reports stopped (no infinite loop). Full OpenRC install/agent/lifecycle/
uninstall e2e recorded in tests/manual/agent-recover-e2e.md section 11.
The installer is now POSIX sh, so the bootstrap pipe should invoke sh
instead of bash — on minimal systems (e.g. Alpine) bash may be absent,
which is exactly the case the rewrite targets. Updates both READMEs, all
docs install snippets (en/zh), and the landing page command.
@ZingerLittleBee ZingerLittleBee merged commit 608cbf6 into main Jun 1, 2026
3 checks passed
@ZingerLittleBee ZingerLittleBee deleted the refactor/install-sh-posix-openrc branch June 1, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant