Skip to content

Add RPM and MSI packaging#16

Merged
kramerc merged 9 commits into
mainfrom
add-packaging
May 14, 2026
Merged

Add RPM and MSI packaging#16
kramerc merged 9 commits into
mainfrom
add-packaging

Conversation

@kramerc

@kramerc kramerc commented May 14, 2026

Copy link
Copy Markdown
Owner

Summary

Adds per-user packaging for Linux (RPM) and Windows (MSI), plus CI that builds both as workflow artifacts on every push to main, every PR, and on demand via workflow_dispatch. Tag pushes additionally publish a GitHub Release with both packages attached. Both packages install per-user without admin/UAC and configure autostart at logon.

  • License: this PR establishes the project's license as MIT — adds a top-level LICENSE file and the license = "MIT" field in Cargo.toml. Previously the project had no declared license, which made downstream use legally ambiguous.
  • Linux: RPM ships /usr/bin/presence-switch + a user-level systemd unit at /usr/lib/systemd/user/presence-switch.service. Built with cargo-generate-rpm on ubuntu-latest — no .spec file, no rpmbuild, no Fedora container. Resulting .rpm installs on any RPM-based distro with systemd and glibc ≥ 2.39 (Fedora 41+, RHEL 10+, recent openSUSE, etc.) since none of the install paths or Requires are Fedora-specific. Package metadata (name, version, license, summary, URL) is inherited from Cargo.toml; the install asset list lives under [package.metadata.generate-rpm].
  • Windows: MSI installs to %LOCALAPPDATA%\PresenceSwitch\, adds an HKCU Run-key entry for autostart at logon, and a Start-menu shortcut. Built on a Linux runner via mingw-w64 cross-compile + wixl from msitools — no Windows runner, no .NET, no WiX EULA. (Scheduled-Task autostart was attempted but schtasks /Create /SC ONLOGON requires admin even for the current user's own task, so it's incompatible with a non-elevated per-user MSI.)
  • Local builds: scripts/package.sh rpm / msi / all mirrors the CI steps. Self-contains its PATH so it doesn't depend on the user's shell rc.
  • Versioning: Cargo.toml's version field is the single source of truth. CI threads it into both the RPM and the MSI; bumping it propagates to both packages with no other edits required. Dev artifacts are tagged with release = "0.dev.<run_number>.git<sha>" (RPM) and <version>-<sha>.msi (Windows).
  • Release-tag handling: pushing a v* tag (e.g. v0.1.0) validates that the tag matches Cargo.toml's version, builds both packages with a clean release tag, and publishes a GitHub Release with gh release create --generate-notes --verify-tag. Mismatched tag → loud fail before any work runs. README has a Releasing section documenting the bump → merge → tag flow. The release flow itself will be validated whenever the first tagged release is cut — not part of this PR's scope.

Quick deviation worth noting:

  • The MSI uses the Windows Installer default progress UI rather than a WixUI_Minimal wizard. wixl 0.106 ships the WiX 4-schema dialog files but emits some Control attributes incorrectly, hitting a libmsi INVALID_FIELD write error at MSI write time. Switching to a hand-written UI fragment or a Windows runner with real WiX would re-enable the wizard later if wanted.

Test plan

  • scripts/package.sh rpm produces a valid .rpm locally; rpm -qip / rpm -qlp show correct metadata and /usr/bin/presence-switch + user-unit file list
  • scripts/package.sh msi produces a valid .msi locally; msiinfo confirms File / Component / Registry tables are populated and the autostart entry is registered under HKCU\Software\Microsoft\Windows\CurrentVersion\Run
  • MSI installs cleanly on Windows 11 (verified via msiexec /i ... /l*v); no UAC prompt; HKCU Run-key entry created
  • CI workflow runs both jobs on this PR and uploads .rpm + .msi artifacts
  • Downloaded artifacts install and run on a fresh Fedora host and Windows host respectively

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 14, 2026 16:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds per-user packaging for Fedora (RPM) and Windows (MSI), plus a GitHub Actions workflow that produces both as artifacts on every push to main, on PRs, and on demand. Both packages install without admin/UAC and configure autostart at logon (systemd user unit on Linux, HKCU Run key on Windows). The PR also adds a top-level LICENSE, Cargo.toml metadata, README install docs, and a local scripts/package.sh helper that mirrors the CI build steps.

Changes:

  • New RPM packaging: packaging/linux/rpm/presence-switch.spec + user systemd unit, built in a fedora:latest container with vendored crates for offline rpmbuild.
  • New MSI packaging: wix/main.wxs consumed by wixl (msitools) on a Linux runner, with the binary cross-compiled via mingw-w64; installs per-user to %LOCALAPPDATA%\PresenceSwitch\ and registers an HKCU Run-key autostart.
  • Tooling/docs: scripts/package.sh {rpm|msi|all}, .github/workflows/package.yml, LICENSE, Cargo.toml metadata, README install section, /vendor added to .gitignore.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
wix/main.wxs New WiX 3 / wixl source defining per-user MSI with HKCU autostart and Start-menu shortcut.
packaging/linux/rpm/presence-switch.spec New RPM spec with vendored-crates offline build and user-unit install.
packaging/linux/systemd/presence-switch.service New per-user systemd unit started after graphical-session.target.
scripts/package.sh Local build helper mirroring CI for rpm, msi, all.
.github/workflows/package.yml New package workflow with rpm (Fedora container) and msi (Linux cross-compile) jobs uploading artifacts.
README.md Adds Installing section, package usage instructions, and License section.
Cargo.toml Adds license, description, repository metadata.
LICENSE Adds MIT license file.
.gitignore Ignores /vendor produced by cargo vendor.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread README.md Outdated
Comment thread wix/main.wxs Outdated
Comment thread packaging/linux/systemd/presence-switch.service Outdated
Comment thread scripts/package.sh Outdated
Comment thread scripts/package.sh Outdated
Comment thread packaging/linux/rpm/presence-switch.spec Outdated
Comment thread scripts/package.sh Outdated
Comment thread .github/workflows/package.yml Outdated
Comment thread .github/workflows/package.yml Outdated
Comment thread packaging/linux/rpm/presence-switch.spec Outdated
kramerc and others added 2 commits May 14, 2026 10:09
- LICENSE (MIT) + Cargo.toml metadata (license, description, repository)
- packaging/linux/systemd/presence-switch.service: per-user unit installed
  to /usr/lib/systemd/user/, started via `systemctl --user enable --now`
- packaging/linux/rpm/presence-switch.spec: offline RPM build with vendored
  crate dependencies; Release: %{?_release} pattern keeps dev builds sorting
  below a future tagged release
- wix/main.wxs: WiX 3 / wixl-compatible MSI, per-user install to
  %LOCALAPPDATA%, HKCU Run-key autostart (schtasks /SC ONLOGON requires
  admin so isn't usable from a non-elevated per-user MSI)
- scripts/package.sh: local build orchestrator with rpm / msi / all
  subcommands; self-contains its PATH for rustup + dotnet tools
- .github/workflows/package.yml: builds .rpm in fedora container and .msi
  via mingw-w64 cross-compile + wixl on ubuntu-latest, both as workflow
  artifacts on push to main, PR, or workflow_dispatch

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kramerc and others added 6 commits May 14, 2026 10:11
- wix/main.wxs: thread Version through $(var.Version) instead of hardcoding
  0.1.0, so MajorUpgrade detection stays correct as Cargo.toml version bumps
- README: describe the HKCU Run-key autostart instead of the (rejected)
  Scheduled Task approach
- systemd unit: drop ProtectSystem=strict (too aggressive for user units —
  it makes $HOME read-only, which would break any future state-write)
- scripts/package.sh: drop dead $HOME/.dotnet/tools PATH entry (wixl is
  apt/dnf-installed, not a dotnet global tool); replace brittle sed line
  numbers in usage() with a heredoc; pass -D Version=$VERSION to wixl;
  trap to clean up ./vendor after the rpm build
- .github/workflows/package.yml: pass -D Version to wixl in CI as well

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Trigger on push to `v*` tags in addition to main / PR / dispatch.
- Tag pushes validate `tag_version == Cargo.toml version` and fail loudly
  if mismatched; otherwise they emit clean release artifacts (RPM
  `Release: 1%{?dist}`, MSI filename without short-sha suffix).
- New `release` job: runs only on tag push, depends on both build jobs,
  downloads their artifacts, and publishes a GitHub Release for the tag
  with `gh release create --generate-notes --verify-tag`.
- README: add Releasing section documenting the bump → merge → tag flow,
  and point the Installing section at Releases for tagged builds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the spec hardcoded `Version: 0.1.0`, so future version bumps
would either silently ship the wrong NEVRA or require touching the spec
every release. Mirror the wxs `$(var.Version)` pattern: the spec now
declares `Version: %{_version}`, and both `scripts/package.sh` and
`.github/workflows/package.yml` pass `--define "_version $VERSION"`
sourced from Cargo.toml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops the spec file + rpmbuild pipeline in favor of a Cargo.toml-native
package definition. Net result: ~40 fewer lines, ~50% fewer moving parts.

- Cargo.toml gains a `[package.metadata.generate-rpm]` block listing the
  install assets (binary, systemd user unit, LICENSE, README). name,
  version, license, summary, and url are inherited from `[package]`.
- The rpm job no longer runs in a `fedora:latest` container; runs on
  plain `ubuntu-latest` like the msi job. Both jobs are now symmetric:
  cargo build → cargo-{generate-rpm|wix-equivalent}.
- Drops: vendor offline-build setup, source-and-vendor tarball assembly,
  rpmdev-setuptree, rpmbuild, --define _version/_release plumbing,
  the spec file itself, the now-empty packaging/linux/rpm/ directory.
- Release tag for dev builds is supplied at invocation time via
  `cargo generate-rpm -s 'release = "0.dev.<N>.git<SHA>"'`; for tagged
  releases the default `release = "1"` from Cargo.toml metadata is used.
- Local script and README updated to reference target/generate-rpm/.

What we give up: debuginfo subpackages (rpmbuild auto-generated, not
useful for end-user distribution of a small daemon) and the `%{?dist}`
suffix in the Release field (only relevant for per-Fedora-version
artifacts, which we're not shipping).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The cargo-generate-rpm switch removed the Fedora-specific bits (no
%{?dist} stamping, no systemd-rpm-macros, no fedora:latest container),
so the produced RPM works on any RPM distro with systemd and a
compatible glibc. Update the README install section to call that out
explicitly: list dnf and zypper examples, note the glibc 2.39 baseline
from the Ubuntu-24.04-based CI runner, and broaden the section title.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kramerc kramerc changed the title Add RPM (Fedora) and MSI (Windows) packaging Add RPM and MSI packaging May 14, 2026
GitHub deprecated Node 20 for JavaScript actions; v4 of checkout/upload/
download-artifact run on Node 20 and warn loudly. They'll be force-flipped
to Node 24 on 2026-06-02 anyway, but the cleaner fix is to pin the newer
majors that ship with Node 24 baked in.

- actions/checkout@v4 → v6 (Nov 2025)
- actions/upload-artifact@v4 → v7 (Apr 2026)
- actions/download-artifact@v4 → v8 (Mar 2026)

Swatinem/rust-cache@v2 is already on Node 24 (v2.9.x runs node24), and
dtolnay/rust-toolchain is a composite action with no JS runtime concern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kramerc kramerc merged commit f329718 into main May 14, 2026
5 checks passed
@kramerc kramerc deleted the add-packaging branch May 14, 2026 18:42
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.

2 participants