mirror of
https://github.com/outbackdingo/hyprcosmic.git
synced 2026-08-25 07:10:09 +00:00
Build installable packages in CI, and replace COSMIC rather than sit beside it
Bumps both forks to the commits that install at upstream's paths, and adds the packaging that follows from it. packaging/ holds one definition per distribution -- an RPM spec, a PKGBUILD and a Debian control template -- and each wraps a tree that `just install` has already staged rather than compiling again inside the packaging tool. Building 27 Rust components a second time to produce bytes that already exist costs hours and creates a way for the packaged desktop and the built one to drift apart. One package per distribution, not one per component. Fedora splits COSMIC into 27 packages, which is right for a distribution tracking upstream. This is a fork that replaces the desktop as a unit: there is no supported combination in which you take the HyprCosmic cosmic-comp and the distribution's cosmic-session, and one package says so accurately. All three declare a conflict with the distribution's cosmic-comp and cosmic-session, and stop rather than resolve it. Obsoletes would let a routine install quietly remove the desktop the machine is currently running; removing COSMIC stays a decision a person makes. .github/workflows/packages.yml builds all three, each inside a container of the distribution it targets, because nothing here is statically linked and a package built elsewhere records sonames the installing machine will not have. It runs on tags and on demand rather than on every push -- three full desktop builds is hours of runner time for artifacts nobody downloads -- and a tag opens a draft release, not a published one. The RPM file list is generated from the staged tree, and claims a directory only when no package already owns it. A naive list would have the package own /usr, /usr/bin and /usr/share, which belong to `filesystem`. README follows the same reframe: the install goes to /usr/bin, the package route is documented first, and the conflict is explained where a reader meets it rather than left to be discovered.
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
# Build installable HyprCosmic packages for Fedora, Arch and Debian.
|
||||
#
|
||||
# WHY THIS IS A SEPARATE WORKFLOW FROM hyprcosmic.yml
|
||||
# --------------------------------------------------
|
||||
# hyprcosmic.yml beside it answers "does the fork still build and do the assets
|
||||
# still install where they claim to", on every push, in a few minutes. This one
|
||||
# compiles 27 Rust components three times over and takes hours. Sharing a file
|
||||
# would mean either running the slow thing on every push or never running the
|
||||
# fast thing on a tag, and a `if:` guard threaded through a shared matrix to
|
||||
# avoid that is harder to read than two files.
|
||||
#
|
||||
# WHY IT DOES NOT RUN ON EVERY PUSH
|
||||
# ---------------------------------
|
||||
# Three full desktop builds per commit is hours of runner time to produce
|
||||
# artifacts nobody downloads. Tags get packages because that is when a package
|
||||
# means something; workflow_dispatch covers wanting one at any other time.
|
||||
#
|
||||
# WHY THE WHOLE JOB RUNS IN A CONTAINER
|
||||
# -------------------------------------
|
||||
# Nothing here is statically linked, so a package is only valid on the
|
||||
# distribution that built it -- an RPM built on Ubuntu's runner would name
|
||||
# Ubuntu's sonames and refuse to install on Fedora. `container:` puts the
|
||||
# compile, the staging and the package build all inside an image of the target,
|
||||
# so the sonames recorded are the ones that will exist on the machine installing
|
||||
# it. This is also why no step here runs on a developer's workstation: the
|
||||
# distribution being targeted is rarely the one being typed at.
|
||||
name: Packages
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to stamp on the packages'
|
||||
required: false
|
||||
default: '0.1.0'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
jobs:
|
||||
package:
|
||||
name: ${{ matrix.distro }}
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.image }}
|
||||
strategy:
|
||||
# One distribution failing on a package name is worth seeing on its own,
|
||||
# and the other two artifacts are still worth having.
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- distro: fedora
|
||||
image: fedora:44
|
||||
- distro: arch
|
||||
image: archlinux:base-devel
|
||||
# trixie rather than the bookworm the other workflows use. bookworm has
|
||||
# no `just` package -- it arrived in trixie -- and its rustc is 1.63,
|
||||
# so `just` would have to be compiled by a toolchain installed before
|
||||
# the thing that installs toolchains. CI pays that to prove the crate
|
||||
# builds on the oldest supported Debian; a package has no such point
|
||||
# to make.
|
||||
- distro: debian
|
||||
image: debian:trixie
|
||||
|
||||
steps:
|
||||
# Before checkout, deliberately: actions/checkout needs git in the image
|
||||
# and these are bare.
|
||||
#
|
||||
# The library lists are upstream's own, from the components' debian/control
|
||||
# files, translated per distribution rather than trimmed.
|
||||
- name: Install build dependencies (fedora)
|
||||
if: matrix.distro == 'fedora'
|
||||
run: |
|
||||
dnf -y install --setopt=install_weak_deps=False \
|
||||
git curl ca-certificates just \
|
||||
gcc gcc-c++ cmake pkgconf-pkg-config nasm lld mold \
|
||||
clang-devel llvm-devel \
|
||||
desktop-file-utils rpm-build \
|
||||
dbus-devel expat-devel fontconfig-devel freetype-devel \
|
||||
libinput-devel libseat-devel libxkbcommon-devel \
|
||||
mesa-libgbm-devel libglvnd-devel wayland-devel libdisplay-info-devel \
|
||||
pixman-devel cairo-devel pango-devel gtk3-devel gtk4-devel \
|
||||
pipewire-devel pulseaudio-libs-devel \
|
||||
gstreamer1-devel gstreamer1-plugins-base-devel \
|
||||
flatpak-devel systemd-devel libgudev-devel \
|
||||
openssl-devel pam-devel libxml2-devel xkeyboard-config-devel
|
||||
|
||||
# Shorter than the others, and not by omission: Arch ships headers in the
|
||||
# main package rather than splitting a -devel, so `wayland` here is
|
||||
# `wayland-devel` on Fedora.
|
||||
- name: Install build dependencies (arch)
|
||||
if: matrix.distro == 'arch'
|
||||
run: |
|
||||
pacman -Syu --noconfirm --needed \
|
||||
git curl just cmake pkgconf nasm lld mold clang llvm \
|
||||
desktop-file-utils sudo \
|
||||
dbus expat fontconfig freetype2 \
|
||||
libinput seatd libxkbcommon \
|
||||
mesa libglvnd wayland libdisplay-info \
|
||||
pixman cairo pango gtk3 gtk4 \
|
||||
pipewire libpulse gst-plugins-base-libs \
|
||||
flatpak systemd-libs libgudev \
|
||||
openssl pam libxml2 xkeyboard-config
|
||||
|
||||
- name: Install build dependencies (debian)
|
||||
if: matrix.distro == 'debian'
|
||||
run: |
|
||||
apt-get update
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
git curl ca-certificates just \
|
||||
build-essential cmake pkg-config nasm lld mold \
|
||||
clang libclang-dev llvm-dev \
|
||||
desktop-file-utils dpkg-dev fakeroot \
|
||||
libdbus-1-dev libexpat1-dev libfontconfig-dev libfreetype-dev \
|
||||
libinput-dev libseat-dev libxkbcommon-dev \
|
||||
libgbm-dev libegl-dev libgles-dev libwayland-dev libdisplay-info-dev \
|
||||
libpixman-1-dev libcairo2-dev libpango1.0-dev libgtk-3-dev libgtk-4-dev \
|
||||
libpipewire-0.3-dev libpulse-dev \
|
||||
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
|
||||
libflatpak-dev libsystemd-dev libudev-dev libgudev-1.0-dev \
|
||||
libssl-dev libpam0g-dev libxml2-dev xkb-data libxcb1-dev
|
||||
|
||||
# submodules: recursive is the whole point -- this repository is 27
|
||||
# components plus the two forks, and a checkout without them builds
|
||||
# nothing.
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# --default-toolchain none, then let cosmic-comp's rust-toolchain.toml
|
||||
# choose. Naming a version here would create a second place to update it
|
||||
# and a way for the packages to be compiled by something the project does
|
||||
# not otherwise use.
|
||||
- name: Install Rust
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
||||
| sh -s -- -y --default-toolchain none --profile minimal
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Show toolchain
|
||||
run: |
|
||||
rustup show
|
||||
cargo --version
|
||||
just --version
|
||||
|
||||
# Keyed per distribution: the same crate compiled against three different
|
||||
# glibcs produces three different artifacts, and sharing one cache between
|
||||
# them would mean each job invalidating the other two.
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: pkg-${{ matrix.distro }}
|
||||
|
||||
- name: Determine version
|
||||
id: ver
|
||||
run: |
|
||||
set -eux
|
||||
# A tag is authoritative; a manual run uses its input; anything else
|
||||
# falls back so the job is still testable from a branch.
|
||||
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
|
||||
v="${GITHUB_REF_NAME#v}"
|
||||
else
|
||||
v="${{ inputs.version || '0.1.0' }}"
|
||||
fi
|
||||
echo "version=$v" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build
|
||||
run: just build
|
||||
|
||||
# prefix=/usr, not the justfile's /usr/local default. Both .desktop files
|
||||
# name an absolute Exec under /usr/bin -- a desktop entry cannot
|
||||
# interpolate a prefix -- so any other prefix stages entries pointing at
|
||||
# paths this step did not write.
|
||||
- name: Stage the install
|
||||
run: just install "$PWD/stage" /usr
|
||||
|
||||
# The staged tree is what all three packages wrap, so it is worth failing
|
||||
# here rather than shipping a package that is missing the compositor. The
|
||||
# negative assertion is the one that would rot quietly: nothing may return
|
||||
# to the private libexec layout this fork used to install into, because a
|
||||
# copy there is a second compositor that nothing runs and no uninstall
|
||||
# removes.
|
||||
- name: Assert the staged tree is a complete desktop
|
||||
run: |
|
||||
set -eux
|
||||
test -x stage/usr/bin/cosmic-comp
|
||||
test -x stage/usr/bin/cosmic-session
|
||||
test -x stage/usr/bin/cosmic-conf
|
||||
test -x stage/usr/bin/start-hyprcosmic
|
||||
test -x stage/usr/bin/start-cosmic
|
||||
test -f stage/usr/share/wayland-sessions/hyprcosmic.desktop
|
||||
test -f stage/usr/share/wayland-sessions/cosmic.desktop
|
||||
test -f stage/usr/lib/systemd/user/cosmic-session.target
|
||||
test -f stage/usr/share/cosmic/com.system76.CosmicSettings.Shortcuts/v1/defaults
|
||||
test -d stage/usr/share/hyprcosmic
|
||||
test ! -e stage/usr/libexec/hyprcosmic
|
||||
echo "staged files: $(find stage -type f | wc -l)"
|
||||
|
||||
# ---- Fedora -------------------------------------------------------
|
||||
#
|
||||
# The file list is generated rather than written into the spec. Across 27
|
||||
# components a hand-maintained %files would be stale within a week, and
|
||||
# stale in the direction that omits files nobody misses until a login
|
||||
# fails.
|
||||
#
|
||||
# Directories need care. A %dir line for every staged directory would
|
||||
# have the package claim /usr, /usr/bin and /usr/share, which the
|
||||
# `filesystem` package owns -- the RPM would build fine and then refuse to
|
||||
# install, or worse, take those directories with it on uninstall. So a
|
||||
# directory is only claimed if no package on the build system already owns
|
||||
# it, which leaves exactly the ones this fork creates
|
||||
# (/usr/share/hyprcosmic and friends).
|
||||
- name: Build the RPM
|
||||
if: matrix.distro == 'fedora'
|
||||
run: |
|
||||
set -eux
|
||||
mkdir -p rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||
: > files.list
|
||||
find stage -mindepth 1 -type d -printf '%P\n' | while read -r d; do
|
||||
rpm -qf --quiet "/$d" || printf '%%%%dir "/%s"\n' "$d" >> files.list
|
||||
done
|
||||
find stage -mindepth 1 \! -type d -printf '"/%P"\n' >> files.list
|
||||
wc -l files.list
|
||||
rpmbuild -bb packaging/fedora/hyprcosmic.spec \
|
||||
--define "_topdir $PWD/rpmbuild" \
|
||||
--define "stagedir $PWD/stage" \
|
||||
--define "filelist $PWD/files.list" \
|
||||
--define "ver ${{ steps.ver.outputs.version }}"
|
||||
mkdir -p dist
|
||||
find rpmbuild/RPMS -name '*.rpm' -exec cp -v {} dist/ \;
|
||||
|
||||
# A package that installs is the claim being made, so it is tested rather
|
||||
# than assumed. --setopt=tsflags=test does the whole resolution and
|
||||
# conflict check without writing to the container.
|
||||
- name: Verify the RPM
|
||||
if: matrix.distro == 'fedora'
|
||||
run: |
|
||||
set -eux
|
||||
rpm -qpi dist/*.rpm
|
||||
rpm -qp --requires dist/*.rpm
|
||||
dnf -y install --setopt=tsflags=test dist/*.rpm
|
||||
|
||||
# ---- Arch ---------------------------------------------------------
|
||||
#
|
||||
# makepkg refuses to run as root, and a container is root by default, so
|
||||
# the build runs as an unprivileged user that owns the tree it reads.
|
||||
# --nodeps because the depends array names a running system's runtime
|
||||
# libraries, which this image has no reason to hold, and nothing is being
|
||||
# compiled at this point anyway.
|
||||
- name: Build the Arch package
|
||||
if: matrix.distro == 'arch'
|
||||
run: |
|
||||
set -eux
|
||||
useradd -m builder
|
||||
mkdir -p dist
|
||||
cp packaging/arch/PKGBUILD .
|
||||
chown -R builder:builder .
|
||||
sudo -u builder \
|
||||
HYPRCOSMIC_STAGEDIR="$PWD/stage" \
|
||||
HYPRCOSMIC_VERSION="${{ steps.ver.outputs.version }}" \
|
||||
PKGDEST="$PWD/dist" \
|
||||
makepkg --nodeps --noconfirm
|
||||
|
||||
- name: Verify the Arch package
|
||||
if: matrix.distro == 'arch'
|
||||
run: |
|
||||
set -eux
|
||||
pacman -Qip dist/*.pkg.tar.zst
|
||||
# Contents rather than an install: pacman has no dry run that resolves
|
||||
# dependencies without touching the filesystem, and this image is not
|
||||
# a desktop, so a real install would fail on runtime libraries that
|
||||
# say nothing about whether the package is well formed.
|
||||
pacman -Qlp dist/*.pkg.tar.zst | head -20
|
||||
|
||||
# ---- Debian -------------------------------------------------------
|
||||
#
|
||||
# dpkg-deb --build over a staged tree, rather than a full source package.
|
||||
# The Depends line is computed by dpkg-shlibdeps from the binaries
|
||||
# themselves rather than written by hand -- 27 components link against
|
||||
# more libraries than anyone will keep an accurate list of, and a hand
|
||||
# list is wrong in the direction that installs and then fails to start.
|
||||
#
|
||||
# dpkg-shlibdeps insists on a debian/control in the working directory even
|
||||
# when invoked outside a source package, hence the stub.
|
||||
- name: Build the Debian package
|
||||
if: matrix.distro == 'debian'
|
||||
run: |
|
||||
set -eux
|
||||
mkdir -p debian
|
||||
printf 'Source: hyprcosmic\n\nPackage: hyprcosmic\nArchitecture: amd64\n' > debian/control
|
||||
binaries=$(find stage -type f -perm -100 -exec sh -c 'file -b "$1" | grep -q ELF && echo "$1"' _ {} \;)
|
||||
dpkg-shlibdeps -O --ignore-missing-info $binaries > shlibdeps.txt
|
||||
deps=$(sed 's/^shlibs:Depends=//' shlibdeps.txt)
|
||||
size=$(du -sk stage | cut -f1)
|
||||
|
||||
mkdir -p stage/DEBIAN
|
||||
sed -e "s|@VERSION@|${{ steps.ver.outputs.version }}|" \
|
||||
-e "s|@INSTALLED_SIZE@|$size|" \
|
||||
-e "s|@SHLIB_DEPENDS@|$deps|" \
|
||||
packaging/debian/control.in > stage/DEBIAN/control
|
||||
cat stage/DEBIAN/control
|
||||
|
||||
mkdir -p dist
|
||||
dpkg-deb --build --root-owner-group stage \
|
||||
"dist/hyprcosmic_${{ steps.ver.outputs.version }}_amd64.deb"
|
||||
|
||||
- name: Verify the Debian package
|
||||
if: matrix.distro == 'debian'
|
||||
run: |
|
||||
set -eux
|
||||
dpkg-deb --info dist/*.deb
|
||||
dpkg-deb --contents dist/*.deb | head -20
|
||||
# lintian is not installed and would fail this package on a dozen
|
||||
# policy points that do not apply to a desktop fork shipped outside
|
||||
# the archive. What matters here is that dpkg can read it back.
|
||||
dpkg-deb --fsys-tarfile dist/*.deb | tar -tf - >/dev/null
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: hyprcosmic-${{ matrix.distro }}
|
||||
path: dist/
|
||||
retention-days: 30
|
||||
|
||||
# Only on a tag. A dispatch run is for getting artifacts to try, and turning
|
||||
# one into a public release would make every experiment look like a shipped
|
||||
# version.
|
||||
release:
|
||||
needs: package
|
||||
if: github.ref_type == 'tag'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Attach the packages to the release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: dist/*
|
||||
# Draft, deliberately. These packages conflict with the
|
||||
# distribution's cosmic-comp and cosmic-session, so installing one
|
||||
# replaces the machine's desktop. That is worth a human reading the
|
||||
# notes before it is published rather than a tag push making it
|
||||
# available.
|
||||
draft: true
|
||||
generate_release_notes: true
|
||||
@@ -18,10 +18,12 @@ Three things distinguish a HyprCosmic session from a COSMIC one:
|
||||
- **HyDE's shell.** waybar instead of cosmic-panel, rofi instead of
|
||||
cosmic-launcher, `awww` instead of cosmic-bg. HyDE themes are imported
|
||||
directly, palette and wallpapers and all.
|
||||
- **It installs beside COSMIC, not over it.** The two modified binaries go to
|
||||
`/usr/libexec/hyprcosmic/`, and the session gets its own entry on the
|
||||
greeter's menu. A machine with COSMIC from its distribution keeps that session
|
||||
working, which matters on the day this one does not start.
|
||||
- **It replaces COSMIC rather than sitting next to it.** The binaries install as
|
||||
`/usr/bin/cosmic-comp` and `/usr/bin/cosmic-session`, the paths a cosmic-comp
|
||||
and a cosmic-session go to, and the packages conflict with the distribution's
|
||||
accordingly. Both session entries are installed, so the greeter still offers a
|
||||
stock COSMIC shell for the day the HyDE one does not start — now served by
|
||||
these binaries rather than by a second copy on disk.
|
||||
|
||||
## Repository layout
|
||||
|
||||
@@ -52,8 +54,8 @@ current.
|
||||
deliberately small: `dispatch exec` and `dispatch killactive` are rejected,
|
||||
because this is the surface any process that can open the socket gets.
|
||||
- New windows open *beside* the focused window rather than inside it.
|
||||
- The install goes to `/usr/libexec/hyprcosmic/cosmic-comp`, and the shortcut
|
||||
defaults file is left alone.
|
||||
- The install goes to `/usr/bin/cosmic-comp`, at upstream's paths and alongside
|
||||
upstream's two `.ron` defaults files, which are carried unmodified.
|
||||
|
||||
**cosmic-session** — profiles. `HYPRCOSMIC_PROFILE=hyprcosmic` (set by
|
||||
`hyprcosmic.desktop`) skips cosmic-panel, cosmic-launcher, cosmic-app-library,
|
||||
@@ -81,6 +83,27 @@ trixie; `cargo install just --locked` covers it.
|
||||
|
||||
## Installing
|
||||
|
||||
The easiest route is a package. Every tag builds one for Fedora, Arch and Debian
|
||||
and attaches it to a draft release; `workflow_dispatch` on **Packages** builds
|
||||
them at any other time and leaves them as run artifacts.
|
||||
|
||||
```shell
|
||||
sudo dnf install ./hyprcosmic-*.rpm # Fedora
|
||||
sudo pacman -U ./hyprcosmic-*.pkg.tar.zst # Arch
|
||||
sudo dpkg -i ./hyprcosmic_*_amd64.deb # Debian
|
||||
```
|
||||
|
||||
Expect this to fail the first time, and read what it says when it does. These
|
||||
packages provide `/usr/bin/cosmic-comp` and `/usr/bin/cosmic-session`, so they
|
||||
**conflict with the distribution's `cosmic-comp` and `cosmic-session`** and your
|
||||
package manager will refuse until those are removed. That refusal is the design:
|
||||
installing HyprCosmic replaces the machine's desktop, and it should take a
|
||||
deliberate `dnf remove cosmic-comp cosmic-session` to say so rather than a
|
||||
resolver deciding on your behalf. Both session entries survive the swap, so the
|
||||
greeter still offers a stock COSMIC shell afterwards.
|
||||
|
||||
Building it yourself instead:
|
||||
|
||||
```shell
|
||||
sudo just install '' /usr
|
||||
```
|
||||
@@ -225,9 +248,18 @@ Two workflows, on purpose:
|
||||
and an `install-assets.sh` round trip into a staging root, verified with
|
||||
`--check`.
|
||||
|
||||
The two forks carry a `hyprcosmic.yml` of the same shape, each asserting that
|
||||
its install landed in `/usr/libexec/hyprcosmic/` and that the files owned by the
|
||||
distribution's COSMIC packages went unwritten.
|
||||
The two forks carry a `hyprcosmic.yml` of the same shape, each building on
|
||||
Fedora, Debian and Arch and asserting that its install landed at upstream's
|
||||
paths — and that nothing landed in the private `/usr/libexec/hyprcosmic/` this
|
||||
fork used to use, which is the assertion that would otherwise rot quietly.
|
||||
|
||||
`packages.yml` in this repository builds installable packages for the same three
|
||||
distributions: an RPM, a `.pkg.tar.zst` and a `.deb`, each compiled inside a
|
||||
container of the distribution it targets so the sonames it records are the ones
|
||||
the installing machine will have. It runs on tags and on demand, not on every
|
||||
push — three full desktop builds is hours of runner time. Tags additionally open
|
||||
a **draft** release with the packages attached; drafts rather than published,
|
||||
because installing one of these replaces the machine's desktop.
|
||||
|
||||
The waybar generator deserves its own note. `config.jsonc` is generated from
|
||||
`config.jsonc.in` and a codepoint table in `generate-config.py`, and is never
|
||||
|
||||
+1
-1
Submodule cosmic-comp updated: 139a1de4e8...5f176995dc
+1
-1
Submodule cosmic-session updated: 8ff1e72d3e...73867a5d6a
@@ -0,0 +1,60 @@
|
||||
# HyprCosmic, as one Arch package.
|
||||
#
|
||||
# There is no build() here, and no source array. This PKGBUILD packages a tree
|
||||
# that `just install` has already staged, which tools/make-packages.sh points at
|
||||
# through $HYPRCOSMIC_STAGEDIR. The reasoning is in packaging/fedora/hyprcosmic.spec
|
||||
# and applies identically: building 27 Rust components a second time under
|
||||
# makepkg, to produce bytes that already exist, costs hours and creates a way
|
||||
# for the packaged desktop and the built one to drift apart.
|
||||
#
|
||||
# The consequence, same as there: this package is only valid on the Arch that
|
||||
# built it. make-packages.sh builds it inside archlinux:base-devel so that is a
|
||||
# current Arch rather than whatever the host is.
|
||||
|
||||
pkgname=hyprcosmic
|
||||
pkgver=${HYPRCOSMIC_VERSION:-0.1.0}
|
||||
pkgrel=1
|
||||
pkgdesc="COSMIC configured in Hyprland's idiom, with a HyDE shell"
|
||||
arch=('x86_64')
|
||||
url="https://github.com/outbackdingo/hyprcosmic"
|
||||
license=('GPL-3.0-only')
|
||||
|
||||
# The HyDE shell. Without these the session starts to a blank screen: no bar,
|
||||
# no launcher, no wallpaper.
|
||||
depends=('waybar' 'rofi-wayland' 'wayland' 'libxkbcommon' 'libinput' 'seatd'
|
||||
'mesa' 'pixman' 'libdisplay-info' 'systemd-libs')
|
||||
|
||||
# awww is in the AUR rather than in the repositories, so it cannot be a hard
|
||||
# depends without making the package uninstallable for anyone who has not built
|
||||
# it. Without it there is no wallpaper, which is recoverable; an unsatisfiable
|
||||
# dependency is not.
|
||||
optdepends=('awww: wallpaper daemon, required for HyDE theme wallpapers'
|
||||
'ttf-nerd-fonts-symbols: glyphs the waybar config draws with'
|
||||
'qt5ct: Qt application theming to match the GTK theme')
|
||||
|
||||
# What "complete replacement" means in packaging terms. Every path this writes
|
||||
# under /usr/bin is one cosmic-comp and cosmic-session also own, so the two
|
||||
# cannot coexist -- which is correct, they are two builds of the same programs.
|
||||
#
|
||||
# conflicts without replaces/provides-driven auto-removal: pacman stops and
|
||||
# names the conflict rather than quietly removing the desktop the machine is
|
||||
# currently running.
|
||||
conflicts=('cosmic-comp' 'cosmic-session')
|
||||
provides=("cosmic-comp=$pkgver" "cosmic-session=$pkgver")
|
||||
|
||||
options=('!strip' '!debug')
|
||||
|
||||
package() {
|
||||
# Set by make-packages.sh. Failing loudly here beats producing an empty
|
||||
# package, which is what a bare `cp -a "$unset/."` would do.
|
||||
if [ -z "$HYPRCOSMIC_STAGEDIR" ] || [ ! -d "$HYPRCOSMIC_STAGEDIR/usr" ]; then
|
||||
echo "HYPRCOSMIC_STAGEDIR unset or has no usr/; see tools/make-packages.sh" >&2
|
||||
return 1
|
||||
fi
|
||||
cp -a "$HYPRCOSMIC_STAGEDIR/." "$pkgdir/"
|
||||
|
||||
# The two files a broken install shows up in first: a bad Exec line puts an
|
||||
# entry on the greeter's menu that fails silently when it is chosen.
|
||||
desktop-file-validate "$pkgdir/usr/share/wayland-sessions/hyprcosmic.desktop"
|
||||
desktop-file-validate "$pkgdir/usr/share/wayland-sessions/cosmic.desktop"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
Package: hyprcosmic
|
||||
Version: @VERSION@
|
||||
Architecture: amd64
|
||||
Maintainer: dingo <[email protected]>
|
||||
Section: x11
|
||||
Priority: optional
|
||||
Homepage: https://github.com/outbackdingo/hyprcosmic
|
||||
Installed-Size: @INSTALLED_SIZE@
|
||||
Depends: @SHLIB_DEPENDS@
|
||||
Recommends: waybar, rofi
|
||||
Suggests: fonts-hack-ttf, qt5ct
|
||||
Conflicts: cosmic-comp, cosmic-session
|
||||
Provides: cosmic-comp, cosmic-session
|
||||
Replaces: cosmic-comp, cosmic-session
|
||||
Description: COSMIC configured in Hyprland's idiom, with a HyDE shell
|
||||
HyprCosmic is a fork of the COSMIC desktop that takes its configuration in
|
||||
Hyprland's idiom and wears a HyDE-style shell.
|
||||
.
|
||||
A single ~/.config/hyprcosmic/cosmic.conf -- with general { } blocks, bind =
|
||||
lines and $variables -- is compiled into COSMIC's own configuration tree by
|
||||
cosmic-conf. The file is the source of truth: keys it names are applied at
|
||||
every login over whatever the settings UI last stored, and keys it does not
|
||||
name are left alone.
|
||||
.
|
||||
The shell is waybar, rofi and awww in place of cosmic-panel, cosmic-launcher
|
||||
and cosmic-bg, and HyDE themes are imported directly. The compositor is
|
||||
COSMIC's, with a Hyprland-compatible IPC socket so that HyDE's scripts and
|
||||
waybar's hyprland modules work unmodified.
|
||||
.
|
||||
This package replaces the distribution's COSMIC. It installs both session
|
||||
entries, so the greeter offers a stock COSMIC shell as well as the HyDE one,
|
||||
both served by these binaries.
|
||||
@@ -0,0 +1,123 @@
|
||||
# HyprCosmic, as one RPM.
|
||||
#
|
||||
# WHY THIS REPACKS RATHER THAN REBUILDS
|
||||
# -------------------------------------
|
||||
# There is no %build here. The spec packages a tree that `just install` has
|
||||
# already produced, which .github/workflows/packages.yml stages and passes in as
|
||||
# --define "stagedir ...".
|
||||
#
|
||||
# The alternative -- a spec that runs `just build` itself under rpmbuild -- is
|
||||
# the more orthodox shape and is wrong for this project. `just build` compiles
|
||||
# 27 Rust components; doing it a second time inside rpmbuild to produce bytes
|
||||
# identical to the ones already sitting in the tree costs hours and buys
|
||||
# nothing. Worse, it would let the packaged desktop and the `just install`
|
||||
# desktop drift apart, and the whole point of shipping a package is that the
|
||||
# two are the same thing.
|
||||
#
|
||||
# The cost of this choice, stated plainly: the resulting RPM is only valid on
|
||||
# the distribution it was built on. Nothing here is statically linked, so an RPM
|
||||
# built on Fedora 44 assumes Fedora 44's glibc, wayland, libinput and mesa. Build
|
||||
# it on the release you intend to install it on. The workflow does that by
|
||||
# running the whole job inside a container of the target distribution.
|
||||
#
|
||||
# WHY IT IS ONE PACKAGE AND NOT TWENTY-SEVEN
|
||||
# ------------------------------------------
|
||||
# Fedora splits COSMIC into a package per component, which is right for a
|
||||
# distribution tracking upstream. This is a fork that replaces the desktop as a
|
||||
# unit: the compositor, the session and the config compiler are versioned and
|
||||
# tested together, and there is no supported combination in which you take the
|
||||
# HyprCosmic cosmic-comp and the distribution's cosmic-session. One package is
|
||||
# an accurate description of what is actually supported.
|
||||
|
||||
%global _hardened_build 1
|
||||
|
||||
# Debuginfo extraction re-links every binary in the tree and would add an hour
|
||||
# to a package whose binaries were built elsewhere anyway. There is nothing to
|
||||
# strip usefully here.
|
||||
%global debug_package %{nil}
|
||||
|
||||
Name: hyprcosmic
|
||||
Version: %{?ver}%{!?ver:0.1.0}
|
||||
Release: %{?rel}%{!?rel:1}%{?dist}
|
||||
Summary: COSMIC configured in Hyprland's idiom, with a HyDE shell
|
||||
|
||||
License: GPL-3.0-only
|
||||
URL: https://github.com/outbackdingo/hyprcosmic
|
||||
BuildArch: x86_64
|
||||
|
||||
# These are what "complete replacement" means in packaging terms. Every path
|
||||
# this package writes under /usr/bin and /usr/share/cosmic is owned by one of
|
||||
# these on a stock Fedora, so the two cannot be installed at once -- which is
|
||||
# correct, because they are two builds of the same programs.
|
||||
#
|
||||
# Conflicts rather than Obsoletes, deliberately. Obsoletes would let a routine
|
||||
# `dnf install hyprcosmic` quietly remove the desktop the machine is currently
|
||||
# running. Conflicts stops and says so, and removing the COSMIC packages stays
|
||||
# something a person decides to do rather than something a resolver does on
|
||||
# their behalf.
|
||||
Conflicts: cosmic-comp
|
||||
Conflicts: cosmic-session
|
||||
|
||||
# What it stands in for, so anything depending on a COSMIC session is satisfied.
|
||||
Provides: cosmic-comp = %{version}-%{release}
|
||||
Provides: cosmic-session = %{version}-%{release}
|
||||
|
||||
# The HyDE shell. These are separate programs this fork drives rather than
|
||||
# builds, and without them the session starts to a blank screen with no bar and
|
||||
# no launcher.
|
||||
Requires: waybar
|
||||
Requires: rofi-wayland
|
||||
|
||||
# The wallpaper daemon. Recommends rather than Requires because it lives in the
|
||||
# alebastr/sway-extras COPR rather than in Fedora proper, and a hard dependency
|
||||
# that cannot resolve would make this package uninstallable on a machine that
|
||||
# has not enabled that repository. Without it you get no wallpaper; with it and
|
||||
# no theme imported, you also get no wallpaper. Both are recoverable; an
|
||||
# unsatisfiable dependency is not.
|
||||
Recommends: awww
|
||||
|
||||
# Nerd Font glyphs are most of what the bar draws.
|
||||
Recommends: nerd-fonts
|
||||
|
||||
%description
|
||||
HyprCosmic is a fork of the COSMIC desktop that takes its configuration in
|
||||
Hyprland's idiom and wears a HyDE-style shell.
|
||||
|
||||
A single ~/.config/hyprcosmic/cosmic.conf -- with general { } blocks, bind =
|
||||
lines and $variables -- is compiled into COSMIC's own configuration tree by
|
||||
cosmic-conf. The file is the source of truth: keys it names are applied at every
|
||||
login over whatever the settings UI last stored, and keys it does not name are
|
||||
left alone.
|
||||
|
||||
The shell is waybar, rofi and awww in place of cosmic-panel, cosmic-launcher and
|
||||
cosmic-bg, and HyDE themes are imported directly. The compositor is COSMIC's,
|
||||
with a Hyprland-compatible IPC socket so that HyDE's scripts and waybar's
|
||||
hyprland modules work unmodified.
|
||||
|
||||
This package replaces the distribution's COSMIC. It installs both session
|
||||
entries, so the greeter offers a stock COSMIC shell as well as the HyDE one,
|
||||
both served by these binaries.
|
||||
|
||||
%prep
|
||||
# Nothing to unpack. See the note at the top of this file.
|
||||
|
||||
%install
|
||||
test -n "%{stagedir}" || { echo "define stagedir: see .github/workflows/packages.yml" >&2; exit 1; }
|
||||
test -d "%{stagedir}/usr" || { echo "%{stagedir}/usr missing; run just install first" >&2; exit 1; }
|
||||
cp -a "%{stagedir}/." "%{buildroot}/"
|
||||
|
||||
# The desktop entries are the two files a broken install shows up in first, so
|
||||
# they are validated rather than assumed. A .desktop with a bad Exec line puts
|
||||
# an entry on the greeter's menu that fails silently when chosen.
|
||||
desktop-file-validate "%{buildroot}%{_datadir}/wayland-sessions/hyprcosmic.desktop"
|
||||
desktop-file-validate "%{buildroot}%{_datadir}/wayland-sessions/cosmic.desktop"
|
||||
|
||||
# Generated by the workflow from the staged tree rather than written out here.
|
||||
# A hand-maintained list across 27 components would be wrong within a week, and
|
||||
# wrong in the direction that ships a package missing files nobody notices until
|
||||
# a login fails.
|
||||
%files -f %{filelist}
|
||||
|
||||
%changelog
|
||||
* Sun Aug 10 2026 dingo <[email protected]> - 0.1.0-1
|
||||
- First package of the fork: COSMIC replaced as a unit, HyDE shell, cosmic-conf.
|
||||
Reference in New Issue
Block a user