From e5ba33d81024a855d36f3d1bfc21f7935c236016 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 21:40:03 +0800 Subject: [PATCH] Pin ecosystem crates and switch to bread-utils::singleton Repoint bread-theme and bread-utils at git.breadway.dev tag v0.7.1 (no github.com). bread-screenshots is not on that tag yet, so rev-pin it instead of tracking branch=main. Replace the homegrown PID-file toggle with bread-utils::singleton so the launcher still dismisses on a second keybind without the TOCTOU window. Screenshot mode still skips the lock. README now documents the fixed BOS dark fallback (not Catppuccin Mocha). Drop the unused GitHub Actions release workflow; Forgejo is canonical. CONTRIBUTING already documents single-trunk. --- .github/README.md | 1 + .github/workflows/release.yml | 67 ----------------------------------- Cargo.lock | 23 ++++++++---- README.md | 2 +- breadbox/Cargo.toml | 7 ++-- breadbox/src/main.rs | 60 ++++++++++--------------------- 6 files changed, 42 insertions(+), 118 deletions(-) create mode 100644 .github/README.md delete mode 100644 .github/workflows/release.yml diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 0000000..4cb7ee5 --- /dev/null +++ b/.github/README.md @@ -0,0 +1 @@ +Forgejo (`.forgejo/workflows`) is the canonical CI. This `.github` tree is unused. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 71c0ca3..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: release - -on: - push: - tags: ["v*"] - -permissions: - contents: write - -env: - DL_DIR: /srv/breadway-dl - ECOSYSTEM_DIR: /home/breadway/Projects/bread-ecosystem - -jobs: - build: - runs-on: [self-hosted, hestia] - steps: - - uses: actions/checkout@v4 - - - name: install build deps - run: sudo apt-get install -y libgtk-4-dev librsvg2-dev libdbus-1-dev pkg-config 2>/dev/null || true - - - name: build - run: cargo build --release --locked - - - name: prepare artifacts - run: | - VERSION="${GITHUB_REF_NAME#v}" - PKG_DIR="${DL_DIR}/breadbox/${VERSION}" - mkdir -p "${PKG_DIR}" - for bin in breadbox breadbox-sync; do - cp "target/release/${bin}" "${PKG_DIR}/${bin}-x86_64" - strip "${PKG_DIR}/${bin}-x86_64" - sha256sum "${PKG_DIR}/${bin}-x86_64" | awk '{print $1}' \ - > "${PKG_DIR}/${bin}-x86_64.sha256" - done - cp packaging/breadbox-sync.service "${PKG_DIR}/" - cp config.example.toml "${PKG_DIR}/" - cp bakery.toml "${PKG_DIR}/bakery.toml" - ln -sfn "${VERSION}" "${DL_DIR}/breadbox/latest" - - - name: ensure bread-ecosystem - run: | - if [[ -d "${ECOSYSTEM_DIR}/.git" ]]; then - git -C "${ECOSYSTEM_DIR}" pull --ff-only - else - mkdir -p "$(dirname "${ECOSYSTEM_DIR}")" - git clone https://github.com/Breadway/bread-ecosystem.git "${ECOSYSTEM_DIR}" - fi - - - name: regenerate index.json - run: bash "${ECOSYSTEM_DIR}/scripts/gen-index.sh" - - - name: upload to GitHub Release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - VERSION="${GITHUB_REF_NAME#v}" - PKG_DIR="${DL_DIR}/breadbox/${VERSION}" - gh release create "${GITHUB_REF_NAME}" \ - --title "breadbox v${VERSION}" --generate-notes 2>/dev/null || true - gh release upload "${GITHUB_REF_NAME}" \ - "${PKG_DIR}/breadbox-x86_64" \ - "${PKG_DIR}/breadbox-sync-x86_64" \ - "${PKG_DIR}/breadbox-x86_64.sha256" \ - "${PKG_DIR}/breadbox-sync-x86_64.sha256" \ - --clobber diff --git a/Cargo.lock b/Cargo.lock index fbbe7cd..d05d5ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,17 +85,17 @@ checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "bread-screenshots" version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=main#f86e299f4a0ea73ff485cd84923b986ddcc8242e" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d#69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" dependencies = [ "anyhow", - "bread-utils", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d)", "tracing", ] [[package]] name = "bread-theme" -version = "0.2.3" -source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.8#77417d552130281ff787e07d52541eb25e9d533b" +version = "0.3.1" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" dependencies = [ "dirs", "gtk4", @@ -106,7 +106,17 @@ dependencies = [ [[package]] name = "bread-utils" version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=main#f86e299f4a0ea73ff485cd84923b986ddcc8242e" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" +dependencies = [ + "dirs", + "serde", + "serde_json", +] + +[[package]] +name = "bread-utils" +version = "0.3.1" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d#69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" dependencies = [ "dirs", "serde", @@ -120,6 +130,7 @@ dependencies = [ "anyhow", "bread-screenshots", "bread-theme", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", "breadbox-shared", "clap", "gtk4", @@ -480,7 +491,7 @@ dependencies = [ "gobject-sys", "libc", "system-deps", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/README.md b/README.md index 2b6ce13..b2d4f32 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ breadbox GTK4 layer-shell launcher - Reads the active Hyprland workspace and sorts apps by context priority - Fuzzy filtering as you type; Enter launches, Escape closes - App icons loaded from the resolved icon cache (see `breadbox-sync`) -- pywal palette auto-detected from `~/.cache/wal/colors.json`, falls back to Catppuccin Mocha +- pywal accents from `~/.cache/wal/colors.json`; background/surface/overlay/foreground stay fixed BOS dark - User CSS override at `~/.config/breadbox/style.css` - Toggle/dismiss: running a second instance kills the first diff --git a/breadbox/Cargo.toml b/breadbox/Cargo.toml index c2d859e..acc4146 100644 --- a/breadbox/Cargo.toml +++ b/breadbox/Cargo.toml @@ -9,9 +9,12 @@ name = "breadbox" path = "src/main.rs" [dependencies] -bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.8", features = ["gtk"] } +bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["gtk"] } +bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1" } # Capture primitives for `--screenshot` mode — see src/screenshot.rs. -bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", branch = "main" } +# Not on tag v0.7.1 (crate landed after that tag); rev-pin so this is not +# `branch = "main"`. +bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", rev = "69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" } breadbox-shared = { path = "../breadbox-shared" } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" diff --git a/breadbox/src/main.rs b/breadbox/src/main.rs index 785ccf9..2de4682 100644 --- a/breadbox/src/main.rs +++ b/breadbox/src/main.rs @@ -254,40 +254,6 @@ fn fuzzy_score(query: &str, entry: &DesktopEntry) -> u32 { 4 // subsequence match } -// ---- PID file toggle -------------------------------------------------------- - -fn pid_file() -> PathBuf { - env::var("XDG_RUNTIME_DIR") - .map(PathBuf::from) - .unwrap_or_else(|_| PathBuf::from("/tmp")) - .join("breadbox.pid") -} - -fn is_breadbox_pid(pid: u32) -> bool { - fs::read_to_string(format!("/proc/{}/comm", pid)) - .map(|s| s.trim() == "breadbox") - .unwrap_or(false) -} - -// Returns false if an existing instance was killed (caller should exit). -fn toggle_or_continue() -> bool { - let pf = pid_file(); - if let Ok(content) = fs::read_to_string(&pf) { - if let Ok(pid) = content.trim().parse::() { - if is_breadbox_pid(pid) { - let _ = Command::new("kill").arg(pid.to_string()).status(); - return false; - } - } - } - let _ = fs::write(&pf, std::process::id().to_string()); - true -} - -fn cleanup_pid() { - let _ = fs::remove_file(pid_file()); -} - // ---- UI --------------------------------------------------------------------- fn get_row_entry(row: >k4::ListBoxRow) -> Option { @@ -344,7 +310,6 @@ fn run_ui( let close_all: Rc = Rc::new({ let w = window.clone(); move || { - cleanup_pid(); w.close(); } }); @@ -551,8 +516,6 @@ fn run_ui( }); window.add_controller(outside_click); - window.connect_destroy(|_| cleanup_pid()); - if let Some(req) = screenshot_req.clone() { screenshot::dispatch(&window, req); } @@ -578,14 +541,27 @@ fn main() { let cli = screenshot::Cli::parse(); let screenshot_req = cli.screenshot_request(); - // The PID-file toggle kills whatever's holding the file — a real, - // already-running breadbox instance included. A screenshot run must + // `toggle_or_kill` kills whatever's holding the single-instance lock — + // a real, already-running breadbox included. A screenshot run must // never touch it: it's a separate, disposable instance by design (same // reasoning as breadbar's `allow_multiple_instances`), not a toggle of // the operator's real launcher. - if screenshot_req.is_none() && !toggle_or_continue() { - return; - } + // + // Kept alive for the rest of `main` — dropping it releases the + // single-instance lock and removes the pid file, which happens + // naturally once `run_ui` returns (after the window closes). + let _singleton_guard = if screenshot_req.is_some() { + None + } else { + match bread_utils::singleton::toggle_or_kill("breadbox") { + Ok(bread_utils::singleton::Toggle::Started(guard)) => Some(guard), + Ok(bread_utils::singleton::Toggle::KilledExisting) => return, + Err(e) => { + eprintln!("breadbox: single-instance lock unavailable ({e}); continuing without it"); + None + } + } + }; let config = Config::load(); let workspace = get_active_workspace().unwrap_or_default();