Package as an RPM, built and published by Forgejo Actions
All checks were successful
RPM / rpm (push) Successful in 1m14s

packaging/ builds it in a Fedora container and makes an RPM;
.forgejo/workflows/rpm.yml runs that on each push to main and uploads it
to the forge's quine RPM group, which quine-linux's image installs from.

lib/main.dart: find pkg-history next to the executable, where the RPM
installs it, before the working directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Philip Peterson 2026-10-31 03:53:51 -08:00
parent 50669e79c5
commit 53d9e56bc2
4 changed files with 157 additions and 2 deletions

View file

@ -0,0 +1,52 @@
name: RPM
# Builds dnf-package-store as an RPM in a Fedora container and uploads it to the
# forge's `quine` RPM group,
# https://forge.quinefoundation.com/api/packages/quine-global-labs/rpm/quine,
# which quine-linux's image installs it from.
#
# Secret: REGISTRY_TOKEN, a Forgejo token with package write access.
# Variable (optional): REGISTRY_USER, the token's owner (default: the actor).
on:
push:
branches: [main]
workflow_dispatch:
concurrency:
group: rpm-${{ github.ref }}
cancel-in-progress: true
jobs:
rpm:
runs-on: runner-x86-linux
steps:
- uses: https://forge.quinefoundation.com/quine-global-labs/mirror-actions-checkout@v4
with:
fetch-depth: 0
- name: Build
# /var/cache/quine-rpm-build outlives the job (host-mode runner): the Flutter SDK and pub packages.
run: |
mkdir -p out /var/cache/quine-rpm-build
podman run --rm -v "$PWD":/src -v /var/cache/quine-rpm-build:/cache -v "$PWD/out":/out \
registry.fedoraproject.org/fedora:44 bash /src/packaging/build-flutter-rpm.sh dnf-package-store
ls -l out
- name: Upload to the quine RPM group
env:
REGISTRY_USER: ${{ vars.REGISTRY_USER || github.actor }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
[ -n "$REGISTRY_TOKEN" ] || { echo "::error::no REGISTRY_TOKEN secret (org or repo)"; exit 1; }
netrc=$(mktemp); trap 'rm -f "$netrc"' EXIT; chmod 600 "$netrc"
printf 'machine %s login %s password %s\n' "${GITHUB_SERVER_URL#https://}" "$REGISTRY_USER" "$REGISTRY_TOKEN" >"$netrc"
for f in out/*.rpm; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --netrc-file "$netrc" --upload-file "$f" \
"$GITHUB_SERVER_URL/api/packages/quine-global-labs/rpm/quine/upload")
echo "$code ${f#out/}"
[ "$code" = 201 ] || [ "$code" = 409 ]
done
- name: Clean up
if: always()
run: rm -rf out build

View file

@ -85,9 +85,14 @@ class _SearchPageState extends State<SearchPage> {
/// Package history lives outside this app now (see `pkg-history` at the /// Package history lives outside this app now (see `pkg-history` at the
/// repo root) — it's a plain script reading rpm-ostree deployments, with /// repo root) — it's a plain script reading rpm-ostree deployments, with
/// no Flutter/GTK build required. This just opens it in a terminal. /// no Flutter/GTK build required. This just opens it in a terminal. It's
/// next to the executable when installed (the RPM puts both in
/// /usr/libexec/dnf-package-store/), or in the working directory when run
/// from a checkout (run.sh).
Future<void> _openHistory() async { Future<void> _openHistory() async {
final script = File('pkg-history').absolute.path; final installed = '${File(Platform.resolvedExecutable).parent.path}/pkg-history';
final script =
await File(installed).exists() ? installed : File('pkg-history').absolute.path;
if (!await File(script).exists()) { if (!await File(script).exists()) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context) ScaffoldMessenger.of(context)

44
packaging/build-flutter-rpm.sh Executable file
View file

@ -0,0 +1,44 @@
#!/usr/bin/env bash
# Builds the Flutter Linux release bundle and packages it as an RPM
# (packaging/$NAME.spec). Runs in a Fedora container (.forgejo/workflows/rpm.yml)
# with this repo at /src, a cache that outlives the job at /cache (the
# Flutter SDK and pub packages), and the RPMs going to /out.
#
# podman run --rm -v "$PWD":/src -v /var/cache/quine-rpm-build:/cache \
# -v "$PWD/out":/out registry.fedoraproject.org/fedora:44 \
# bash /src/packaging/build-flutter-rpm.sh dnf-package-store
set -euo pipefail
NAME=$1
# Flutter's stable release, pinned (storage.googleapis.com/flutter_infra_release/releases/releases_linux.json).
FLUTTER_VERSION=3.47.9
FLUTTER_SHA256=25ed8c3c29d51a7353f2a944310b478af8214920639524b19c50a19e6c044c5a
dnf install -y --setopt=install_weak_deps=False \
clang cmake ninja-build pkgconf-pkg-config gtk3-devel xz-devel libstdc++-devel \
git xz which unzip rpm-build desktop-file-utils
sdk=/cache/flutter-$FLUTTER_VERSION
if [ ! -x "$sdk/bin/flutter" ]; then
curl -fL --retry 3 -o /cache/flutter.tar.xz \
"https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_$FLUTTER_VERSION-stable.tar.xz"
echo "$FLUTTER_SHA256 /cache/flutter.tar.xz" | sha256sum -c
rm -rf "$sdk.tmp" && mkdir -p "$sdk.tmp"
tar -xJf /cache/flutter.tar.xz -C "$sdk.tmp" --strip-components=1
mv "$sdk.tmp" "$sdk" && rm /cache/flutter.tar.xz
fi
# The SDK and this checkout belong to other users than the container's root.
git config --global --add safe.directory '*'
export PUB_CACHE=/cache/pub PATH="$sdk/bin:$PATH"
flutter --disable-analytics >/dev/null 2>&1 || true
cd /src
flutter build linux --release
version=$(sed -n 's/^version: *\([0-9][0-9.]*\).*/\1/p' pubspec.yaml)
release=$(date -u +%Y%m%d%H%M).git$(git rev-parse --short=7 HEAD)
rpmbuild -bb "packaging/$NAME.spec" \
--define "_topdir /tmp/rpmbuild" \
--define "version_ $version" --define "release_ $release" \
--define "bundle /src/build/linux/x64/release/bundle" --define "srcdir /src"
cp -v /tmp/rpmbuild/RPMS/*/*.rpm /out/

View file

@ -0,0 +1,54 @@
# Built by packaging/build-flutter-rpm.sh from Flutter's release bundle; not
# a from-source spec (no %prep/%build).
%global debug_package %{nil}
# The bundle's own libraries (libflutter_linux_gtk.so, plugins) are private,
# and the executable links them from its own lib/, so no automatic
# dependencies from the bundle: the Requires below are what it needs from the
# system (the image build checks every library resolves).
%global __provides_exclude_from ^%{_libexecdir}/%{name}/.*$
%global __requires_exclude_from ^%{_libexecdir}/%{name}/.*$
Name: dnf-package-store
Version: %{version_}
Release: %{release_}%{?dist}
Summary: Browse DNF packages and try them out with transient installs
License: LicenseRef-Not-Specified
URL: https://forge.quinefoundation.com/quine-global-labs/dnf-package-store
ExclusiveArch: x86_64
Requires: gtk3
Requires: polkit
Requires: dnf5
Requires: python3
Requires: rpm-ostree
%description
A desktop app for browsing the package catalog of DNF-based systems and
installing packages transiently (dnf install --transient), so test installs
disappear on reboot. pkg-history lists the system's rpm-ostree deployments.
%install
mkdir -p %{buildroot}%{_libexecdir}/%{name} %{buildroot}%{_bindir}
cp -a %{bundle}/. %{buildroot}%{_libexecdir}/%{name}/
# Next to the executable, where the app looks for pkg-history (lib/main.dart).
install -m 0755 %{srcdir}/pkg-history %{srcdir}/pkg-apply %{buildroot}%{_libexecdir}/%{name}/
ln -s ../libexec/%{name}/dnf_package_store %{buildroot}%{_bindir}/%{name}
ln -s ../libexec/%{name}/pkg-history %{buildroot}%{_bindir}/pkg-history
mkdir -p %{buildroot}%{_datadir}/applications
cat > %{buildroot}%{_datadir}/applications/com.ironmagma.dnf_package_store.desktop <<'DESKTOP'
[Desktop Entry]
Type=Application
Name=DNF Package Store
Comment=Browse packages and try them out without keeping them
Exec=dnf-package-store
Icon=system-software-install
Categories=System;PackageManager;
StartupWMClass=com.ironmagma.dnf_package_store
DESKTOP
desktop-file-validate %{buildroot}%{_datadir}/applications/com.ironmagma.dnf_package_store.desktop
%files
%{_libexecdir}/%{name}
%{_bindir}/%{name}
%{_bindir}/pkg-history
%{_datadir}/applications/com.ironmagma.dnf_package_store.desktop