From 8ff1e72d3e3943eaf07ec8f546ffac3ba740ac74 Mon Sep 17 00:00:00 2001 From: dingo Date: Mon, 10 Aug 2026 17:13:34 +0700 Subject: [PATCH] test: follow the autostart template off its author's home directory, and let CI compile The template it parses stopped naming /home/dingo, so the two assertions that named it had to move with it: the bar is now an `sh -c` invocation rather than a bare waybar, and the wallpaper path is $HOME. The wallpaper assertion keeps the surrounding double quotes in the expected string deliberately -- dropping them is what would split the path at a space in someone's home directory, which is the failure the test exists to catch. Adds a ratchet: no line in the shipped autostart may contain "/home/". Both lines carried an author's home directory right up until the repository was about to be published, which is a bad moment to find out. CI is fixed at the same time, because it could never have run these two tests. They read ../config/autostart through include_str!, so the crate does not compile its own test profile unless the parent repository is its parent directory -- and a workflow checkout puts this repository at the workspace root, alone. Confirmed by building a standalone export of HEAD: error: couldn't read `.../standalone/../config/autostart`: No such file error: could not compile `cosmic-session` (bin "cosmic-session" test) So the job now checks the parent out first and this repository over the top of where its submodule sits, which is the layout the working tree already has. The coupling is worth keeping: the point of those tests is that the template this session actually ships still parses to the commands it documents, and a copy vendored in here to make the build self-contained would be a copy that goes stale without anyone noticing. The three dependency-install steps override working-directory back to the workspace root, since they run before the checkout that creates the directory the job otherwise defaults to, and a run step whose working-directory does not exist fails before it runs anything. --- .github/workflows/hyprcosmic.yml | 42 ++++++++++++++++++++++++++++++-- src/profile.rs | 31 +++++++++++++++++++++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/.github/workflows/hyprcosmic.yml b/.github/workflows/hyprcosmic.yml index 26d8b80..b5b3720 100644 --- a/.github/workflows/hyprcosmic.yml +++ b/.github/workflows/hyprcosmic.yml @@ -42,10 +42,22 @@ jobs: - distro: arch image: archlinux:latest + # Every `run` step works from the crate, which the checkout below deliberately + # places one level down inside the parent repository rather than at the root. + defaults: + run: + working-directory: hyprcosmic/cosmic-session + steps: # Before checkout: actions/checkout needs git and these images are bare. + # + # The three of them override working-directory back to the workspace root, + # because the default above names a directory the checkout has not created + # yet and a run step whose working-directory does not exist fails before it + # runs anything. - name: Install build dependencies (fedora) if: matrix.distro == 'fedora' + working-directory: . run: dnf -y install --setopt=install_weak_deps=False git curl gcc just # bookworm has no `just` package -- it arrived in trixie -- so it is built @@ -54,6 +66,7 @@ jobs: # is better than a comment explaining an absence somewhere else. - name: Install build dependencies (debian) if: matrix.distro == 'debian' + working-directory: . run: | apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ @@ -61,9 +74,31 @@ jobs: - name: Install build dependencies (arch) if: matrix.distro == 'arch' + working-directory: . run: pacman -Syu --noconfirm --needed git curl base-devel just - - uses: actions/checkout@v4 + # Two checkouts, laid out the way the working tree is laid out. + # + # Two tests here read `../config/autostart` through include_str!, so the + # crate does not compile its own test profile unless the parent repository + # is its parent directory. That coupling is deliberate -- the point of + # those tests is that the template this session actually ships still + # parses to the commands it documents, and a copy vendored in here to make + # the build self-contained would be a copy that silently goes stale. + # + # The parent lands first and this repository is checked out over the top of + # where its submodule would be, so the crate under test is the commit that + # triggered the run and not whatever the parent has pinned. + - name: Check out the parent repository + uses: actions/checkout@v4 + with: + repository: ${{ github.repository_owner }}/hyprcosmic + path: hyprcosmic + + - name: Check out this repository inside it + uses: actions/checkout@v4 + with: + path: hyprcosmic/cosmic-session - name: Install Rust run: | @@ -71,8 +106,11 @@ jobs: | sh -s -- -y --default-toolchain stable --profile minimal echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + # Both paths are workspace-relative, and `defaults.run.working-directory` + # does not apply to actions -- only to `run` steps. - uses: Swatinem/rust-cache@v2 with: + workspaces: hyprcosmic/cosmic-session key: ${{ matrix.distro }} - name: Install just (debian only) @@ -114,7 +152,7 @@ jobs: - uses: actions/upload-artifact@v4 with: name: cosmic-session-${{ matrix.distro }} - path: stage/ + path: hyprcosmic/cosmic-session/stage/ retention-days: 14 # Off the matrix on purpose. Shell scripts do not vary by distribution, and diff --git a/src/profile.rs b/src/profile.rs index b4450d1..a52e68b 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -293,7 +293,9 @@ mod tests { assert_eq!(got.len(), 5, "{got:?}"); // First, so its startup compile lands before the compositor settles. assert_eq!(got[0], vec!["cosmic-conf", "watch"]); - assert_eq!(got[1][0], "waybar"); + // The bar is a shell invocation, because its stylesheet lives under a + // home directory that this file cannot expand on its own. + assert_eq!(got[1][0], "sh"); assert_eq!(got[2], vec!["awww-daemon"]); // Last, so the fallback terminal is the topmost window. assert_eq!(got[4], vec!["cosmic-term"]); @@ -321,8 +323,13 @@ mod tests { assert_eq!(argv.len(), 3, "{argv:?}"); // The symlink, not one of the copies beside it. Naming a copy would // strand this line on a path the next theme import deletes. + // + // `$HOME` rather than a real home directory: sh expands it, and the + // shipped template has to work for whoever installed it, not only for + // whoever wrote it. The double quotes are asserted with it, because + // dropping them is what would split the path at a space in $HOME. assert!( - argv[2].contains(r#""/home/dingo/.local/share/wallpapers/hyprcosmic/current""#), + argv[2].contains(r#""$HOME/.local/share/wallpapers/hyprcosmic/current""#), "{}", argv[2] ); @@ -330,4 +337,24 @@ mod tests { // must not be set until it answers. assert!(argv[2].starts_with("until awww query"), "{}", argv[2]); } + + /// The template is installed verbatim into every user's config directory, + /// so a literal `/home/someone` in it is a file that works for exactly one + /// person -- and it fails quietly for everyone else, because a waybar with + /// an unreadable stylesheet still starts and a wallpaper that was never set + /// looks the same as one that failed to load. + /// + /// Both lines carried an author's home directory until the repository was + /// about to be published. This is here so that neither can carry one again. + #[test] + fn the_shipped_autostart_names_nobodys_home_directory() { + let text = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../config/autostart")); + for (n, line) in text.lines().enumerate() { + assert!( + !line.contains("/home/"), + "config/autostart:{}: absolute home directory: {line}", + n + 1 + ); + } + } }