mirror of
https://github.com/outbackdingo/hyprcosmic.git
synced 2026-08-25 14:53:21 +00:00
557 lines
26 KiB
YAML
557 lines
26 KiB
YAML
# 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 }}
|
|
# GitHub defaults `run:` to `sh -e {0}` inside a container, and on Debian
|
|
# that is dash. Several steps below use brace expansion and process
|
|
# substitution, which dash does not have and which bash-invoked-as-sh
|
|
# disables. Naming bash once here is better than writing POSIX around a
|
|
# constraint no target actually imposes -- all three images ship bash.
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
|
|
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 the union of Build-Depends across all 27 upstream
|
|
# components' debian/control files, translated per distribution rather
|
|
# than trimmed. Derived mechanically rather than assembled by hand, after
|
|
# a hand-assembled list built for eighteen minutes and then failed on
|
|
# dav1d -- a dependency of cosmic-bg that nothing in the obvious set names.
|
|
#
|
|
# The Fedora and Arch translations were checked against dnf repoquery and
|
|
# archlinux.org's package API rather than guessed, because a name that
|
|
# does not exist fails the whole step: it is libdav1d-devel on Fedora, not
|
|
# dav1d-devel, and ttf-opensans on Arch, not otf-opensans.
|
|
#
|
|
# The Fedora list names a set of basic tools the other two get for free.
|
|
# Fedora's container image is deliberately minimal: no `which` (deprecated
|
|
# there in favour of `command -v`), no make, no findutils, no tar. Arch's
|
|
# base-devel and Debian's build-essential plus its essential set include
|
|
# all of them, which is why this only ever broke on one of the three.
|
|
#
|
|
# These are not guesses. The top-level justfile opens with
|
|
# `make := \`which make\``, and the submodules' own recipes shell out to
|
|
# find, xargs, tar and sed -- 114 tar invocations and 18 find between
|
|
# them. Several steps in this workflow use find as well.
|
|
- 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++ make which findutils tar gzip sed diffutils \
|
|
cmake pkgconf-pkg-config nasm lld mold \
|
|
clang-devel llvm-devel \
|
|
desktop-file-utils rpm-build intltool ImageMagick open-sans-fonts \
|
|
dbus-devel expat-devel fontconfig-devel freetype-devel \
|
|
libinput-devel libseat-devel libxkbcommon-devel \
|
|
mesa-libgbm-devel mesa-libEGL-devel libglvnd-devel \
|
|
wayland-devel libdisplay-info-devel libdav1d-devel \
|
|
pixman-devel cairo-devel pango-devel glib2-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 intltool imagemagick ttf-opensans \
|
|
dbus expat fontconfig freetype2 \
|
|
libinput seatd libxkbcommon \
|
|
mesa libglvnd wayland libdisplay-info dav1d \
|
|
pixman cairo pango glib2 gtk3 gtk4 \
|
|
pipewire libpipewire 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 file \
|
|
intltool imagemagick fonts-open-sans \
|
|
libdbus-1-dev libexpat1-dev libfontconfig-dev libfreetype-dev \
|
|
libinput-dev libseat-dev libxkbcommon-dev \
|
|
libgbm-dev libegl-dev libegl1-mesa-dev libgles-dev \
|
|
libwayland-dev libdisplay-info-dev libdav1d-dev \
|
|
libpixman-1-dev libcairo2-dev libpango1.0-dev libglib2.0-dev \
|
|
libgtk-3-dev libgtk-4-dev \
|
|
libpipewire-0.3-dev libspa-0.2-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
|
|
|
|
# stable as the default, not `none`.
|
|
#
|
|
# The two fork workflows use --default-toolchain none and let
|
|
# rust-toolchain.toml decide, which is right there because the checkout
|
|
# root is the crate and the pin sits in it. Here it does not: the pin is
|
|
# cosmic-comp/rust-toolchain.toml, one level down, and this job's other 27
|
|
# components have no pin at all. With `none` there is no default to fall
|
|
# back to and the first cargo invocation at the repository root fails
|
|
# before anything is built.
|
|
#
|
|
# Naming stable does not weaken the pin. rustup applies a directory-local
|
|
# rust-toolchain.toml whenever it enters that directory and installs it on
|
|
# demand, so cosmic-comp still compiles with the 1.93 it asks for while
|
|
# everything else uses stable.
|
|
- name: Install Rust
|
|
run: |
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
|
| sh -s -- -y --default-toolchain stable --profile minimal
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
|
|
- name: Show toolchain
|
|
run: |
|
|
rustup show
|
|
cargo --version
|
|
just --version
|
|
|
|
# No Swatinem/rust-cache here, unlike the two fork workflows.
|
|
#
|
|
# It was tried and it fails on this repository specifically: the action
|
|
# runs `cargo metadata` at the workspace root to work out what to cache,
|
|
# and this root is a meta-repository with no Cargo.toml -- it is 29
|
|
# submodules, each its own crate with its own target/. The action reported
|
|
# `could not find Cargo.toml in /__w/hyprcosmic/hyprcosmic` and cached
|
|
# nothing.
|
|
#
|
|
# Listing all 29 workspaces would fix the error and create a worse
|
|
# problem: their target directories come to roughly 17 GB, against a 10 GB
|
|
# per-repository cache limit, so the jobs would evict each other's entries
|
|
# every run and pay upload time for the privilege. A cold build is the
|
|
# honest cost of a workflow that only runs on tags and on demand.
|
|
- 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 three binaries this fork actually changes, under their own names.
|
|
#
|
|
# Done on every distribution, because start-hyprcosmic and the session
|
|
# entry name these paths and there is no reason for those to differ per
|
|
# distribution. cosmic-session takes the compositor as argv[1], so the
|
|
# pair is arranged in start-hyprcosmic and needs no source change.
|
|
- name: Give this fork's binaries their own names
|
|
run: |
|
|
set -eux
|
|
mv stage/usr/bin/cosmic-comp stage/usr/bin/hyprcosmic-comp
|
|
mv stage/usr/bin/cosmic-session stage/usr/bin/hyprcosmic-session
|
|
mv stage/usr/bin/cosmic-conf stage/usr/bin/hyprcosmic-conf
|
|
|
|
# The stock session entry goes with it, on Debian only.
|
|
#
|
|
# start-cosmic execs /usr/bin/cosmic-session, which the rename above just
|
|
# took away, so cosmic.desktop would sit on the greeter's menu and die
|
|
# silently when chosen. Everywhere else the reduction below removes both
|
|
# and the distribution's own cosmic-session package supplies a working
|
|
# pair; on Debian there is no such package, so they are removed here and
|
|
# the greeter offers the HyprCosmic entry alone.
|
|
- name: Drop the stock session entry it can no longer start
|
|
if: matrix.distro == 'debian'
|
|
run: |
|
|
set -eux
|
|
rm -f stage/usr/bin/start-cosmic
|
|
rm -f stage/usr/share/wayland-sessions/cosmic.desktop
|
|
|
|
# HyprCosmic installs beside COSMIC rather than over it, and this is the
|
|
# step that makes that true.
|
|
#
|
|
# `just install` stages the whole desktop, because it builds the whole
|
|
# tree. Nearly all of it is byte-identical to what the distribution
|
|
# already ships, and the parts that are not are file conflicts that stop
|
|
# the install outright -- 62 of them on a stock Fedora COSMIC, across 25
|
|
# packages, which rpm reports only in the transaction check, long after
|
|
# dnf's dependency solving has said the transaction is fine.
|
|
#
|
|
# The alternative to reducing the tree is to claim all 25 packages with
|
|
# Conflicts, which means erasing them to install this, which on Fedora
|
|
# includes cosmic-greeter -- the display manager. A fork you can only try
|
|
# by removing the desktop you would fall back to is a fork with no way
|
|
# back. So everything the distribution already owns is dropped, and the
|
|
# package depends on the distribution's COSMIC for it.
|
|
#
|
|
# Not on Debian, which has no COSMIC to depend on: neither cosmic-session
|
|
# nor cosmic-comp is packaged there, in any suite. Reducing the tree there
|
|
# would produce a package whose dependency can never be satisfied, so the
|
|
# Debian build keeps the whole desktop it just compiled and stands alone.
|
|
# Revisit when Debian packages COSMIC.
|
|
- name: Reduce the staged tree to this fork's own files
|
|
if: matrix.distro != 'debian'
|
|
run: |
|
|
set -eux
|
|
# /usr/share/cosmic goes too, all of it. Those are the defaults the
|
|
# compositor reads at first run, and this fork carries upstream's
|
|
# copies unmodified -- every one of them is byte-identical to a file a
|
|
# distribution package already owns. rpm permits two packages to own
|
|
# an identical file, so keeping them would install today and then
|
|
# collide the first time the distribution changed one. They arrive
|
|
# with the cosmic-comp this package depends on.
|
|
( cd stage && find . \( -type f -o -type l \) -printf '%P\n' ) |
|
|
while read -r p; do
|
|
case $p in
|
|
usr/bin/hyprcosmic-*|usr/bin/start-hyprcosmic) continue ;;
|
|
usr/share/hyprcosmic/*) continue ;;
|
|
usr/share/wayland-sessions/hyprcosmic.desktop) continue ;;
|
|
esac
|
|
rm -f "stage/$p"
|
|
done
|
|
find stage -type d -empty -delete
|
|
|
|
# Worth failing here rather than shipping a package that is missing the
|
|
# compositor. The negative assertions are the ones that would rot quietly:
|
|
# nothing may return to the private libexec layout this fork used to
|
|
# install into, and no cosmic-* name may come back, because either one is
|
|
# a file conflict that only shows up on a machine that has COSMIC
|
|
# installed -- which is every machine this is meant for.
|
|
- name: Assert the staged tree is this fork and nothing else
|
|
if: matrix.distro != 'debian'
|
|
run: |
|
|
set -eux
|
|
test -x stage/usr/bin/hyprcosmic-comp
|
|
test -x stage/usr/bin/hyprcosmic-session
|
|
test -x stage/usr/bin/hyprcosmic-conf
|
|
test -x stage/usr/bin/start-hyprcosmic
|
|
test -f stage/usr/share/wayland-sessions/hyprcosmic.desktop
|
|
test -d stage/usr/share/hyprcosmic
|
|
test ! -e stage/usr/libexec/hyprcosmic
|
|
test ! -e stage/usr/bin/cosmic-comp
|
|
test ! -e stage/usr/bin/cosmic-session
|
|
test ! -e stage/usr/share/wayland-sessions/cosmic.desktop
|
|
test ! -e stage/usr/share/cosmic
|
|
test -z "$(find stage/usr/bin -mindepth 1 ! -name 'hyprcosmic-*' ! -name 'start-hyprcosmic')"
|
|
echo "staged files: $(find stage -type f | wc -l)"
|
|
|
|
# Debian is not reduced, so the assertion is the opposite one: the package
|
|
# stands alone there and has to carry a desktop that starts. The renamed
|
|
# three must be present under their new names, and the components the
|
|
# session launches must still be in the tree rather than assumed to arrive
|
|
# from a distribution package that does not exist.
|
|
- name: Assert the staged tree is a complete desktop
|
|
if: matrix.distro == 'debian'
|
|
run: |
|
|
set -eux
|
|
test -x stage/usr/bin/hyprcosmic-comp
|
|
test -x stage/usr/bin/hyprcosmic-session
|
|
test -x stage/usr/bin/hyprcosmic-conf
|
|
test -x stage/usr/bin/start-hyprcosmic
|
|
test -f stage/usr/share/wayland-sessions/hyprcosmic.desktop
|
|
test -d stage/usr/share/hyprcosmic
|
|
test -d stage/usr/share/cosmic
|
|
test ! -e stage/usr/libexec/hyprcosmic
|
|
test ! -e stage/usr/bin/cosmic-comp
|
|
test ! -e stage/usr/bin/cosmic-session
|
|
test ! -e stage/usr/bin/start-cosmic
|
|
test ! -e stage/usr/share/wayland-sessions/cosmic.desktop
|
|
for c in cosmic-settings cosmic-settings-daemon cosmic-osd cosmic-notifications; do
|
|
test -x "stage/usr/bin/$c" || { echo "missing $c" >&2; exit 1; }
|
|
done
|
|
echo "staged files: $(find stage -type f | wc -l)"
|
|
|
|
# Checked here, once, rather than in each of the three packaging recipes,
|
|
# and not with desktop-file-validate.
|
|
#
|
|
# desktop-file-validate rejects DesktopNames -- "keys extending the format
|
|
# should start with X-" -- because the Desktop Entry Specification
|
|
# registers keys for application launchers, and these are session files.
|
|
# DesktopNames is what a display manager reads to set XDG_CURRENT_DESKTOP,
|
|
# so the session needs it. cosmic.desktop is upstream cosmic-session's
|
|
# file, unchanged here apart from the Exec path, and the copy Fedora ships
|
|
# as cosmic-session-1.5.0-1.fc44 fails the identical check: the validator
|
|
# has no entry for the key, and every distribution ships the file anyway.
|
|
#
|
|
# What the validator would not have caught is the failure that actually
|
|
# matters: an Exec naming a binary this package does not install puts an
|
|
# entry on the greeter's menu that dies silently when chosen. So that is
|
|
# what is checked, against the tree about to be packaged.
|
|
- name: Check the session entries
|
|
run: |
|
|
set -eu
|
|
for f in stage/usr/share/wayland-sessions/*.desktop; do
|
|
echo "== $f"
|
|
cat "$f"
|
|
test "$(sed -n 1p "$f")" = '[Desktop Entry]' || {
|
|
echo "$f: first line is not [Desktop Entry]" >&2; exit 1; }
|
|
for key in Name Type Exec DesktopNames; do
|
|
grep -q "^${key}=" "$f" || { echo "$f: no $key=" >&2; exit 1; }
|
|
done
|
|
grep -qx 'Type=Application' "$f" || {
|
|
echo "$f: Type is not Application; a greeter will ignore it" >&2; exit 1; }
|
|
exec_path=$(sed -n '0,/^Exec=/s/^Exec=//p' "$f" | cut -d' ' -f1)
|
|
case $exec_path in
|
|
/*) ;;
|
|
*) echo "$f: Exec=$exec_path is not absolute" >&2; exit 1 ;;
|
|
esac
|
|
test -x "stage${exec_path}" || {
|
|
echo "$f: Exec=$exec_path is not an executable this package installs" >&2
|
|
exit 1
|
|
}
|
|
echo " Exec -> stage${exec_path} ok"
|
|
done
|
|
|
|
# ---- 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.
|
|
# To a file and then head: `| head` would kill pacman with SIGPIPE
|
|
# once head has its 20 lines, and these steps run with pipefail.
|
|
pacman -Qlp dist/*.pkg.tar.zst > contents.txt
|
|
echo "entries: $(wc -l < contents.txt)"
|
|
head -20 contents.txt
|
|
|
|
# ---- 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
|
|
# To a file and then head, not `| head`. Actions runs these steps with
|
|
# pipefail, and head closing the pipe after 20 lines kills dpkg-deb
|
|
# with SIGPIPE, which pipefail reports as a failed step -- a green
|
|
# 217 MB package failed here on nothing but that.
|
|
dpkg-deb --contents dist/*.deb > contents.txt
|
|
echo "entries: $(wc -l < contents.txt)"
|
|
head -20 contents.txt
|
|
# 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 install beside the distribution's COSMIC
|
|
# rather than over it, so a bad one costs a logout rather than a
|
|
# desktop -- but a tag push is still not a decision to publish. The
|
|
# generated notes are written by a machine reading commit subjects,
|
|
# and the Debian package differs from the other two in what it
|
|
# carries; both are worth a human reading before anyone downloads.
|
|
draft: true
|
|
generate_release_notes: true
|