Skip to content

feat(packaging): build an .rpm alongside the .deb - #39

Open
gutolatitude wants to merge 2 commits into
ci/release-provenance-weekly-scanfrom
feat/rpm-package
Open

gutolatitude wants to merge 2 commits into
ci/release-provenance-weekly-scanfrom
feat/rpm-package

Conversation

@gutolatitude

@gutolatitude gutolatitude commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

First of three PRs for RPM support (LSH-3178). Stacked on #38 (same files); retarget to main once #37/#38 merge. Nothing is published to a repository yet: that is the next PR. This one builds the .rpm, attaches it to releases, and tests it.

The RPM follows the .deb as-is: same files, same lifecycle, amd64 and arm64.

Changes

  • .goreleaser.yaml: rpm format next to deb. Dependencies and maintainer scripts move to per-format overrides (deb unchanged: ufw, init-system-helpers and the existing scripts; rpm: ufw from EPEL and systemd). File modes are set to 0644 explicitly instead of coming from the checkout's umask.
  • packaging/scripts/rpm/{post,preun,postun}: the .deb behaviour, with RPM's package counts instead of dpkg action names:
    • first install: migrate a source-built install.sh host and enable the unit (RHEL presets would leave it disabled);
    • upgrade: keep the admin's enable/disable choice, restart an enabled agent onto the new binary;
    • removal: stop and disable, drop the /usr/local/bin compatibility link, never touch UFW;
    • /etc/lsh-agent/env stays on removal (RPM has no purge; uninstall.sh removes it).
    • migrate_from_install_sh is the postinst function verbatim; a Bats test fails if they drift.
  • Tests: tests/rpm-scripts.bats (12 cases), scripts/test-rpm-install.sh, and an install-rpm CI job on Alma, Rocky and Oracle 9 and 10, natively on amd64 and arm64: install over a source-built setup (migration), reinstall with a changed config.yaml (kept, %config(noreplace)), remove (ufw and env kept). ShellCheck now lists the package scripts explicitly (the packaging/scripts/* glob would hit the new rpm/ directory).
  • Release: the provenance attestation and SBOMs from ci: release provenance and SBOMs, weekly vulnerability scan; call ufw without sudo as root #38 cover the .rpm files; release notes mention them.

Found while testing

dnf removes packages that came in only as a dependency, so on a host where ufw arrived only with the agent, dnf remove lsh-agent would remove ufw too. install.sh already installs ufw on its own first, so hosts set up by it are safe; the test does the same, and the repository docs in the next PR will say dnf install ufw lsh-agent.

Checked locally

  • Snapshot build: lsh-agent-<ver>~dev-1.{x86_64,aarch64}.rpm next to the .deb files; rpm -qp shows the dependencies, the three scriptlets and config.yaml as %config(noreplace).
  • scripts/test-rpm-install.sh passes on all six images (amd64); arm64 runs in CI.
  • Bats 70/70, ShellCheck, zizmor, actionlint, goreleaser check.

🤖 Generated with Claude Code


Note

Medium Risk
Touches packaging, systemd migration, and uninstall on RHEL-family hosts; wrong scriptlet or dnf flags could break upgrades or leave firewall state inconsistent.

Overview
Adds RPM packages built with GoReleaser next to the existing .deb files (same artifacts, amd64/arm64), with per-format overrides: Debian keeps current deps and postinst/prerm/postrm; RPM uses EPEL ufw plus systemd and new packaging/scripts/rpm/{post,preun,postun} scriptlets that mirror the .deb lifecycle (migrate from install.sh, enable on first install, respect admin enable/disable on upgrade, stop/disable on removal, leave UFW and env file semantics as documented).

CI and release upload and attest .rpm artifacts, generate a second snapshot at 0.0.1 for upgrade testing, and add an install-rpm matrix (Alma/Rocky/Oracle 9–10 × arch) running scripts/test-rpm-install.sh. ShellCheck targets the new script paths explicitly.

uninstall.sh removes lsh-agent via dnf when installed from RPM (clean_requirements_on_remove=False), drops the yum repo file, and Bats assert both apt and dnf failure exits before file cleanup. Docs and release drafter now mention .rpm, SBOM naming, and provenance for RPMs.

Reviewed by Cursor Bugbot for commit 5828144. Bugbot is set up for automated code reviews on this repo. Configure here.

RetriggerView in GreptileConfidence Score: 5/5

The latest changes appear safe to merge, with no new blocking issues found.

Summary

Adds RPM packages alongside Debian packages, with package scripts, release attestations, and installation tests.

  • RHEL-family hosts can install and update the agent from RPM packages.
  • The uninstall script removes RPM installs through dnf.

Reviews (2) · Last reviewed commit: "feat(packaging): uninstall.sh removes th..." · Reviewed by Greptile

The RHEL family gets the same package as Debian/Ubuntu: same files, same
lifecycle, amd64 and arm64.

- .goreleaser.yaml: rpm format; dependencies and maintainer scripts per
  format (rpm: ufw from EPEL + systemd); file modes set explicitly.
- packaging/scripts/rpm/{post,preun,postun}: the .deb behaviour with RPM's
  package counts. Enable on first install, keep the admin's choice on
  upgrade, restart an enabled agent onto the new binary, stop and disable on
  removal, never touch UFW. The install.sh migration is the postinst function
  verbatim (a Bats test keeps them identical). env stays on removal: RPM has
  no purge.
- tests/rpm-scripts.bats, scripts/test-rpm-install.sh and a CI job that
  installs, reinstalls (config kept) and removes the .rpm on Alma, Rocky and
  Oracle 9 and 10, natively on amd64 and arm64.
- The provenance attestation covers the .rpm files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread .goreleaser.yaml Resolved
Comment thread scripts/test-rpm-install.sh Resolved
- uninstall.sh: dnf remove on the RHEL family (keeping ufw), stopping before
  files or UFW are touched if it fails, as with apt purge.
- CI builds the same packages a second time as version 0.0.1, and the
  install-rpm job installs that first and upgrades to the package under
  test: new binary in place, migration link kept, and an agent the admin
  disabled stays disabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gutolatitude

Copy link
Copy Markdown
Contributor Author

@greptileai review

@gutolatitude

Copy link
Copy Markdown
Contributor Author

✅ Validated on real bare metal

The whole stack (#39 package, #40 dnf repository, #41 installer) was validated end to end on real Latitude.sh bare metal, provisioned from the production images. It passes on every EL distro we offer:

  • Rocky Linux 9 and 10
  • AlmaLinux 9 and 10
  • Oracle Linux 9 and 10

Each server went through the same 22 checks:

  • install from the signed dnf repository;
  • firewalld handed over to UFW;
  • firewall rules created through the API and applied by the agent, including rule add and remove;
  • version pin, downgrade and upgrade;
  • reboot persistence, with the metadata service still reachable;
  • uninstall.

The Oracle Linux 10 image was missing a UEK8 kernel module package that UFW needs. That is fixed on the image side in latitudesh/tinkerbell-packer-images#444, so nothing needs to change here.

Full results and notes: #41 (comment)

🤖 Generated with Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant