Add RPM and MSI packaging#16
Merged
Merged
Conversation
There was a problem hiding this comment.
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 afedora:latestcontainer with vendored crates for offlinerpmbuild. - New MSI packaging:
wix/main.wxsconsumed bywixl(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.tomlmetadata, README install section,/vendoradded 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.
- 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>
- 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>
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>
This was referenced May 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 viaworkflow_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.LICENSEfile and thelicense = "MIT"field inCargo.toml. Previously the project had no declared license, which made downstream use legally ambiguous./usr/bin/presence-switch+ a user-level systemd unit at/usr/lib/systemd/user/presence-switch.service. Built withcargo-generate-rpmonubuntu-latest— no.specfile, norpmbuild, no Fedora container. Resulting.rpminstalls on any RPM-based distro with systemd and glibc ≥ 2.39 (Fedora 41+, RHEL 10+, recent openSUSE, etc.) since none of the install paths orRequiresare Fedora-specific. Package metadata (name, version, license, summary, URL) is inherited fromCargo.toml; the install asset list lives under[package.metadata.generate-rpm].%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 +wixlfrommsitools— no Windows runner, no.NET, no WiX EULA. (Scheduled-Task autostart was attempted butschtasks /Create /SC ONLOGONrequires admin even for the current user's own task, so it's incompatible with a non-elevated per-user MSI.)scripts/package.sh rpm/msi/allmirrors the CI steps. Self-contains its PATH so it doesn't depend on the user's shell rc.Cargo.toml'sversionfield 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 withrelease = "0.dev.<run_number>.git<sha>"(RPM) and<version>-<sha>.msi(Windows).v*tag (e.g.v0.1.0) validates that the tag matchesCargo.toml's version, builds both packages with a clean release tag, and publishes a GitHub Release withgh 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:
WixUI_Minimalwizard. wixl 0.106 ships the WiX 4-schema dialog files but emits some Control attributes incorrectly, hitting alibmsi INVALID_FIELDwrite 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 rpmproduces a valid.rpmlocally;rpm -qip/rpm -qlpshow correct metadata and/usr/bin/presence-switch+ user-unit file listscripts/package.sh msiproduces a valid.msilocally;msiinfoconfirms File / Component / Registry tables are populated and the autostart entry is registered underHKCU\Software\Microsoft\Windows\CurrentVersion\Runmsiexec /i ... /l*v); no UAC prompt; HKCU Run-key entry created.rpm+.msiartifacts🤖 Generated with Claude Code