Pin ecosystem crates and switch to bread-utils::singleton
All checks were successful
check / check (push) Successful in 18s

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.
This commit is contained in:
Breadway 2026-08-15 21:40:03 +08:00
parent 3251e18a49
commit e5ba33d810
6 changed files with 42 additions and 118 deletions

1
.github/README.md vendored Normal file
View file

@ -0,0 +1 @@
Forgejo (`.forgejo/workflows`) is the canonical CI. This `.github` tree is unused.

View file

@ -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

23
Cargo.lock generated
View file

@ -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]]

View file

@ -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

View file

@ -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"

View file

@ -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::<u32>() {
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: &gtk4::ListBoxRow) -> Option<DesktopEntry> {
@ -344,7 +310,6 @@ fn run_ui(
let close_all: Rc<dyn Fn()> = 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();