Commit Graph
28 Commits
Author SHA1 Message Date
gitops 28042b9879 Install beside COSMIC instead of replacing it
The packages could not be installed. Claiming the cosmic-* names put 62 files
in conflict with 25 distribution packages -- something dnf's depsolve never
reports, because it shows up only in rpm's transaction check -- and the only
way to satisfy that was Conflicts against all 25, which erases cosmic-greeter.
On a stock Fedora COSMIC that is the display manager, so the machine comes
back to a text console with no way to log in and no session to fall back to.

So the fork ships what it actually forks. The three changed binaries take
hyprcosmic-comp, hyprcosmic-session and hyprcosmic-conf, the workflow reduces
the staged tree to those plus this fork's own assets, and cosmic-settings, the
portal, the OSD and the rest come from the distribution at the version it
tested them at. No Conflicts, no Provides, nothing erased, and the stock
COSMIC entry stays on the greeter's menu to fall back to.

Debian is the exception and keeps the whole desktop, because COSMIC is not
packaged there in any suite: there is nothing to depend on and nothing to
install beside. Its stock session entry is dropped instead, since start-cosmic
execs the cosmic-session this rename takes away.
2026-08-11 14:30:14 +07:00
gitops 49d5f8b20e cosmic-conf: a fixed set of workspaces, with no compositor change
COSMIC's workspaces are dynamic and there is no setting that turns that
off. ensure_last_empty keeps exactly one trailing empty workspace and
collects every other empty one the moment its last window closes, so
"workspace 4 is the browser" is only true while the browser is open.

The primitive that fixes it already exists. can_auto_remove is

    is_empty() && !has_activation_token() && !pinned

and pinned_workspaces is an existing persisted CosmicCompConfig key that
Workspaces::add_output drains into the first output to appear. So this is
a projection and nothing else: no cosmic-comp patch, no new config key,
and the fast cosmic-conf CI job covers all of it rather than a 2.5 hour
package build per distro.

    workspace = 1, name:term
    workspace = 2, name:web
    workspace = 3, name:code

Three things about the restore path shaped the module.

Restore is positional. PinnedWorkspace has no index field -- the order of
the Vec becomes the order of the workspaces -- so `workspace = 4` cannot
emit one entry. It emits four, with 1..3 unnamed, or the browser
workspace comes back as workspace 1. That is why there is a cap: without
one, `workspace = 1000` is a typo that silently creates a thousand
workspaces rather than a diagnostic.

The dynamic workspace survives. Pinned workspaces are pushed into an
empty WorkspaceSet and ensure_last_empty then appends the usual empty
one, so declaring four gives 1-4 always present and a fifth appearing
when you use it. That is Hyprland's behaviour and not a compromise
either way.

It lands at the next login. Workspaces::new reads the key once at
compositor start and there is no reload path for it, while cosmic-conf
watch is started from the autostart file after COSMIC's own components.
Every other key in this file is live, so the one that is not is worth
saying out loud in cosmic.conf rather than leaving to be discovered.

Ids are generated rather than random

random_workspace_id is format!("{:x}", rand(0..2<<24)), at most seven hex
digits. Ours are FNV-1a of the index with the high nibble forced on, so
always eight -- a collision with a compositor-generated id is impossible
by construction rather than unlikely. FNV is written out rather than
reaching for DefaultHasher, which is explicitly not stable across Rust
releases: the id is what ties a window's saved workspace to the
workspace it reappears on, so it changing under a toolchain bump is a
bug that would be very hard to attribute.

monitor: is refused, not ignored

Hyprland's monitor: is the parameter a user reaches for first and the one
COSMIC cannot honour. output_matches compares the EDID before the name,
so an OutputMatch with edid: None is rejected outright against any output
that reports one, and only falls through to the name when neither side
has an EDID. Every real panel reports one, so a name-only match would
work on a VM and nowhere else. cosmic.conf cannot supply an EDID -- it is
a manufacturer triple, product id, serial and manufacture date read off
the wire by the DRM backend.

So the parameter fails with that explanation, the same way follow_mouse =
2 does. Accepting it and quietly doing nothing was the third option and
the worst of the three. The emitted OutputMatch is empty, which is not a
placeholder: an unmatchable match is how a workspace says it has no
output preference, which is the only thing this file can truthfully say.

tiling: defaults to general:autotile

A PinnedWorkspace carries its own tiling_enabled. Defaulting it to false
would have meant that turning workspaces on quietly turned tiling off for
someone whose whole reason for editing the file was autotile = true, so
resolve reads the folded autotile write and passes it through.

CI

The assets job syntax-checked two of the four shell scripts;
hyprcosmic-fan and hyprcosmic-keybinds were added in fc8c2a7 and never
registered. Both are now checked and shellchecked, and each is checked
with the shell its shebang names -- hyprcosmic-fan is POSIX sh on
purpose, and `bash -n` would accept a bashism that fails where /bin/sh is
dash. All four are clean today, so the list stays a ratchet.

Verified

The RON shape was taken from the struct definitions rather than guessed:
PinnedWorkspace has no serde defaults, so all four fields are emitted,
and the (field: value) / None / Some(x) spelling matches what COSMIC
itself wrote to this machine's xkb_config. 19 unit tests in the new
module and 6 in resolve cover the gap-filling, the id range, the
inherited tiling default, the duplicate-index diagnostic, and that a
quote in a name cannot break out of the RON.

Not verified: nothing was compiled here. cargo test, clippy -D warnings
and the release build run in the cosmic-conf job.
2026-08-11 08:02:41 +07:00
gitops 5f521529a8 packages: fix the three packaging failures, none of which were the build
Run 31394740824 compiled all 27 components on all three distributions --
two and a half hours each -- staged them, passed the completeness
assertions, and then failed in the packaging step on every one. Three
separate causes, none of them the desktop.

desktop-file-validate rejects DesktopNames (fedora, arch)

    hyprcosmic.desktop: error: file contains key "DesktopNames" in group
    "Desktop Entry", but keys extending the format should start with "X-"

DesktopNames is the key a display manager reads to set
XDG_CURRENT_DESKTOP, so a session entry needs it, but the Desktop Entry
Specification registers keys for application launchers and the validator
has no entry for it. This is not something the fork introduced:
cosmic.desktop is upstream cosmic-session's file, unchanged apart from the
Exec path, and the copy already installed on this machine as
cosmic-session-1.5.0-1.fc44 fails the identical check. Dropping the key
would satisfy the validator and break the session.

So the validation moves out of the three packaging recipes into one step
against the staged tree, and checks what desktop-file-validate would not
have caught anyway: that Exec is absolute and names an executable this
package actually installs. A wrong Exec puts an entry on the greeter's
menu that dies silently when it is chosen, which is the failure that
matters here; a key the validator has not heard of is not.

The check was run against a staged tree built from the real desktop files
before committing: it passes clean, and it rejects an Exec naming a binary
that is not in the tree, an Exec that exists but is not executable, a
relative Exec, a missing DesktopNames, a missing Name, a missing [Desktop
Entry] header and Type=Link, each with a message saying which file and
what.

SIGPIPE under pipefail (debian, and arch next)

    dpkg-deb: error: tar subprocess was killed by signal (Broken pipe)

The Debian package built: 217 MB, correct control, Depends computed by
dpkg-shlibdeps across 17 libraries. It failed on `dpkg-deb --contents
dist/*.deb | head -20` in the verify step -- head closes the pipe after
twenty lines, dpkg-deb dies of SIGPIPE, and Actions runs these steps with
pipefail. Both now write to a file and head the file. The Arch verify step
had the same line waiting for it and is fixed in the same way.

bogus date in %changelog (fedora)

10 August 2026 is a Monday.

Also: the PKGBUILD still pointed at tools/make-packages.sh, which was
deleted when packaging moved to Actions. It now names the two commands
that stage a tree and build from it by hand.
2026-08-10 23:23:35 +07:00
gitops a397334242 packages: derive the build dependencies instead of assembling them by hand
Run 31383454158, debian job, eighteen minutes in:

    error: failed to run custom build command for `dav1d-sys v0.8.3`
    Package 'dav1d', required by 'virtual:world', not found

dav1d is pulled in by cosmic-bg, and nothing in the obvious set of
compositor dependencies names it. A hand-assembled list will keep having
that shape of hole, and each one costs a full run to find.

So the lists are now the union of Build-Depends across all 27 upstream
components' debian/control files, collected mechanically. That adds
libdav1d-dev, libglib2.0-dev, libspa-0.2-dev, libegl1-mesa-dev,
imagemagick, intltool and fonts-open-sans, none of which were present
before and all of which upstream declares.

The Fedora and Arch translations were verified against `dnf repoquery` and
archlinux.org's package API rather than guessed, because a name that does
not exist fails the whole step and costs another run to discover: it is
libdav1d-devel on Fedora and not dav1d-devel, and ttf-opensans on Arch and
not otf-opensans. Both mistakes were in the first draft of this commit.
2026-08-10 18:48:59 +07:00
gitops 4bedb04f77 packages: name the basic tools Fedora's image does not ship, and drop rust-cache
Run 31383454158, fedora job, first line of the build:

    justfile:3:  make := `which make`
    sh: line 1: which: command not found

Fedora's container image is deliberately minimal and has neither `which`
-- deprecated there in favour of `command -v` -- nor make. Arch's
base-devel and Debian's build-essential include both, which is why this
broke on exactly one of the three.

Rather than fix that one symptom and rediscover the next, the recipes were
read for what they actually shell out to: 360 cargo, 163 install, 114 tar,
26 which, 18 find, 11 sed, 4 xargs, 2 make. findutils and tar are also
absent from Fedora's image, so both would have failed in turn, one run at
a time. All of them are now named, along with gzip, sed and diffutils.

Debian gains `file`, used by the deb step to pick ELF binaries out of the
staged tree for dpkg-shlibdeps. It is not part of Debian's essential set.

rust-cache is removed rather than repaired. It runs `cargo metadata` at the
workspace root to decide what to cache, and this root is a meta-repository
with no Cargo.toml -- 29 submodules, each its own crate -- so it reported
"could not find Cargo.toml" and cached nothing. Listing all 29 workspaces
would fix the error and buy a worse problem: their target directories come
to roughly 17 GB against a 10 GB per-repository limit, so the three jobs
would evict each other every run and pay the upload time for it. A cold
build is the honest cost of a workflow that runs on tags and on demand.
2026-08-10 18:30:32 +07:00
gitops e65a9722d9 packages: give rustup a default, and run steps under bash
Both failures on run 31383204572, all three distributions, in the same
step, before anything was compiled.

--default-toolchain none was copied from the two fork workflows, where it
is correct: their checkout root is the crate, so rust-toolchain.toml sits
in it and rustup reads the pin. This repository's root has no pin -- it is
in cosmic-comp/, one level down, and the other 27 components have none at
all -- so `none` left no default and the first cargo invocation at the
root failed with "could not choose a version of cargo to run".

stable does not weaken the pin. rustup applies a directory-local
rust-toolchain.toml on entering that directory and installs it on demand,
so cosmic-comp still gets the 1.93 it asks for.

The same logs showed `shell: sh -e {0}`, which is the container default
and is dash on Debian. Two later steps use brace expansion, which dash
lacks and bash-as-sh disables, so the RPM build would have created a
directory literally named rpmbuild/{BUILD,RPMS,...} and failed further
along. Declared bash for the job rather than rewriting around a constraint
none of the three images impose.

Worth recording why this was not caught before pushing: the run blocks
were syntax-checked with bash, which is not what was going to run them.
Checking with dash would not have caught it either -- brace expansion
failing is runtime behaviour, not a parse error. Declaring the shell is
the fix; there is no static check that substitutes for it.
2026-08-10 18:25:36 +07:00
gitops d2b091fa8b 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.
2026-08-10 18:22:02 +07:00
gitops 63610d57df ci: give this workflow a name of its own, so it stops cancelling upstream's
Both files said `name: CI`. The concurrency group is built from
${{ github.workflow }}, which is that name, so the two workflows shared a group
and cancelled each other on every push -- whichever started second would be the
only one that ever reported a result. The compositor and session forks already
say HyprCosmic for the same reason; this one was missed when it was renamed.
2026-08-10 17:32:05 +07:00
gitops c6150755d2 Fetch the two forks over HTTPS, and stop cloning 29 submodules to lint CSS
Two consequences of becoming the meta-repo, neither of which the merge itself
could have caught.

The submodule URLs were written as [email protected]: because that is how they are
pushed from here. That is a working copy's business, not the repository's:
upstream names all 29 over HTTPS, a clone with no key configured is the normal
case, and CI has no key at all. The URLs are now HTTPS; each submodule's own
origin stays SSH, which is where it belongs.

The assets job asked for `submodules: recursive` back when this repository had
two of them. It now names 29, and 27 are the rest of COSMIC -- several gigabytes
fetched per run to check that some CSS is in step with its generator. It fetches
the one it needs instead.

That one is cosmic-session, because install-assets.sh also places the session
entry point that lives in that fork. The script skips those two files with a
warning when the checkout is absent rather than failing, so a fetch that
silently stopped working would quietly reduce what the job covers instead of
turning it red. Hence the `test -f` after it.
2026-08-10 17:30:45 +07:00
gitops c0ae1350f3 Merge pop-os/cosmic-epoch: become the meta-repo, with two submodules swapped
HyprCosmic started as a repository beside COSMIC holding a config compiler and a
pile of theme assets, with the two modified components hanging off it. That had
the relationship backwards. COSMIC already has a meta-repo whose whole job is to
name every component and build the desktop from them, so the honest shape for a
fork is to be that meta-repo with the components we changed pointing at our
copies -- not a separate tree that assumes the rest of COSMIC arrived some other
way.

So this merges cosmic-epoch in and repoints exactly two of its 29 submodules:

    cosmic-comp     -> outbackdingo/hyprcosmic-comp
    cosmic-session  -> outbackdingo/hyprcosmic-session

The other 27 stay on pop-os. Nothing about them needs to change, and pinning
them to copies we do not maintain would be a promise to keep 27 forks current.

`just build` and `just install` now build and install the whole desktop with our
compositor and session in it, which is what "fork COSMIC" ought to mean. The two
forks still install into /usr/libexec/hyprcosmic rather than over /usr/bin, so a
system that already has COSMIC from its distribution keeps that session on the
greeter's menu alongside this one.

Conflict resolutions worth stating:

  .github/workflows/ci.yml   upstream's kept as-is. It builds the entire desktop
                             on Arch through `just sysext`, which is precisely
                             the check a meta-repo wants and is not made less
                             useful by forking. Our per-distribution workflow
                             moved to hyprcosmic.yml beside it, the same way it
                             did in the compositor fork.

  .gitmodules                upstream's 29 entries, then two URLs rewritten.

  cosmic-comp, cosmic-session
                             ours. Git reports "no merge base" because they are
                             unrelated to the pop-os commits recorded here,
                             which is expected: they are different repositories,
                             not newer commits of the same one.

  .gitignore                 both sides, plus a note that the two forks are
                             deliberately no longer ignored.
2026-08-10 17:22:10 +07:00
gitops 3c1a092e51 ci: build cosmic-conf per distro, and check the assets against their generators
Two jobs, shaped differently on purpose.

cosmic-conf gets the same three-distribution container matrix the forks use,
plus one check the unit tests cannot make: it resolves the cosmic.conf this
repository actually ships. A schema change that invalidated the shipped config
would pass all 143 tests in the crate and still break every user on their first
login. The binary is invoked directly rather than through `cargo run`, because
the step redirects HOME and cargo keys its registry cache on it -- `cargo run`
would re-download every dependency into a directory the cache action does not
know about.

The assets job is where the rules that were previously only remembered become
enforced. config.jsonc is regenerated and must not move, which is what stops it
being hand-edited; the template and the generator are held to pure ASCII, which
is what stops a Nerd Font glyph being pasted somewhere it will be silently
destroyed by the next person who retypes it. install-assets.sh is then run into
a staging root and asked to verify its own work, which also exercises the audit
that refuses to install at all while any file under config/ is unclassified.

The ASCII check is written as an `if` rather than `! grep`, because grep exits 1
for "no match" and 2 for "no such file" -- negating it would turn a vanished
file into a pass, and the check would quietly stop checking anything.
2026-08-10 16:53:49 +07:00
Jeremy Soller f9abdc105d Add PR template 2026-02-13 12:21:16 -07:00
Vukašin VojinovićandMichael Murphy 7106172bcd chore: update CI 2026-01-14 17:43:39 +01:00
Jack BoswellandGitHub be97a211e1 Update config.yml 2024-11-04 09:32:21 +11:00
Jack BoswellandGitHub ba96994e7d Update config.yml 2024-10-31 23:10:24 +11:00
Jack BoswellandGitHub f8a0f29675 Update bug_report.md 2024-10-31 22:12:02 +11:00
Jack BoswellandGitHub 85a48f0f53 Update config.yml 2024-10-31 22:07:29 +11:00
Jack BoswellandGitHub 5c4fa4287e Update issue templates 2024-10-31 22:06:55 +11:00
Jack BoswellandGitHub afcb876be2 Merge pull request #2 from boswelja/boswelja-patch-1
Create config.yml
2024-10-31 22:04:46 +11:00
Jack BoswellandGitHub 2e7a373ff2 Update issue templates 2024-10-31 22:04:15 +11:00
Jack BoswellandGitHub 8145b20798 Create config.yml 2024-10-31 22:03:12 +11:00
Victoria BrekenfeldandVictoria Brekenfeld 8dec6bd48e ci: Add nasm 2024-02-15 16:21:59 +01:00
Victoria BrekenfeldandVictoria Brekenfeld d1db7c4b7d ci: recurse submodules and add libflatpak 2024-01-31 17:12:06 +01:00
Ron Waldon-HoweandVictoria Brekenfeld cd8dc9d887 chore(ci): cosmic-edit needs gtk3 2023-12-20 14:20:25 +01:00
Ron Waldon-HoweandVictoria Brekenfeld 7ecd86ce28 chore(ci): add missing at-spi2-core package 2023-12-20 14:20:25 +01:00
Victoria Brekenfeld 46fc0bf34d ci: free space for building 2023-08-10 18:38:09 +02:00
75cb3ab49d feat: Add COSMIC Settings (#75)
* feat: Add COSMIC Settings

This add cosmic-settings to the .gitmodules file

* Update justfile per feedback

* Correct cosmic-launcher to cosmic-settings

* fix(settings): build

---------

Co-authored-by: Ashley Wulber <[email protected]>
2023-02-20 11:24:10 -05:00
Ian Douglas Scott 326a80995d Add CI that verifies the repository builds
This runs on Arch, and shows that things build on another distro with
recent dependencies. If we want to support older versions of
dependencies, that will need to be tested on a different platform.

This also shows what dependencies are needed.
2023-02-14 08:59:13 -08:00