From 5a60a769015597c7c5ef2f5f0bc7bf87db6f95dc Mon Sep 17 00:00:00 2001 From: Breadway Date: Tue, 21 Jul 2026 19:19:33 +0800 Subject: [PATCH 01/23] ci: remove GitHub push-mirror workflow --- .forgejo/workflows/mirror.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .forgejo/workflows/mirror.yml diff --git a/.forgejo/workflows/mirror.yml b/.forgejo/workflows/mirror.yml deleted file mode 100644 index 0f8916b..0000000 --- a/.forgejo/workflows/mirror.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Mirror to GitHub - -on: - push: - branches: ['**'] - tags: ['**'] - -jobs: - mirror: - runs-on: [self-hosted, hestia] - steps: - - name: Mirror to GitHub - run: | - set -euo pipefail - git clone --mirror "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" repo.git - cd repo.git - git push --prune \ - "https://x-access-token:${{ secrets.MIRROR_TOKEN }}@github.com/Breadway/breadclip.git" \ - '+refs/heads/*:refs/heads/*' '+refs/tags/*:refs/tags/*' From 34a96b78e02686ae0e7bd593ac038fa4a616c037 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 09:56:49 +0800 Subject: [PATCH 02/23] ci: add dev/beta build track workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds dev-release.yml (publishes on every push to dev) and beta-release.yml (publishes on a beta-v* tag), mirroring the pattern landing in bread-ecosystem/bread. Also creates the dev branch for this repo, which didn't exist before — see bread-ecosystem/docs/release-channels.md for the three-track policy. --- .forgejo/workflows/beta-release.yml | 56 +++++++++++++++++++++++++ .forgejo/workflows/dev-release.yml | 65 +++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 .forgejo/workflows/beta-release.yml create mode 100644 .forgejo/workflows/dev-release.yml diff --git a/.forgejo/workflows/beta-release.yml b/.forgejo/workflows/beta-release.yml new file mode 100644 index 0000000..e1aaac6 --- /dev/null +++ b/.forgejo/workflows/beta-release.yml @@ -0,0 +1,56 @@ +name: beta release + +# Publishes a beta-track build when a `beta-v*` tag is pushed — +# separate from release.yml's tag-triggered stable releases. See +# bread-ecosystem's docs/release-channels.md for the three-track policy +# this is part of. +on: + push: + tags: ['beta-v*'] + +jobs: + build: + runs-on: [self-hosted, hestia] + steps: + - name: checkout + run: | + set -euo pipefail + rm -rf src && mkdir src + git clone --branch "${GITHUB_REF_NAME}" --depth 1 \ + "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src + + - name: build + run: cd src && cargo build --release --locked + + - name: prepare artifacts + run: | + set -euo pipefail + VERSION="${GITHUB_REF_NAME#beta-v}" + PKG_DIR="/srv/breadway-dl/beta/breadclip/${VERSION}" + mkdir -p "${PKG_DIR}" + for bin in breadclip breadclipd; do + cp "src/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 src/contrib/breadclipd.service "${PKG_DIR}/" + cp src/bakery.toml "${PKG_DIR}/bakery.toml" + ln -sfn "${VERSION}" "/srv/breadway-dl/beta/breadclip/latest" + + # No GitHub Release upload — beta, like the other non-stable track, + # is only distributed via dl.breadway.dev/beta/. + - name: regenerate beta index.json + env: + MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }} + run: | + set -euo pipefail + if [ -z "${MINISIGN_SEC_KEY:-}" ]; then + echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate beta index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone on the beta track)" + exit 1 + fi + rm -rf /tmp/bread-ecosystem-ci + # --branch dev: the TRACK-aware gen-index.sh isn't merged to + # bread-ecosystem's main yet. + git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci + TRACK=beta bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh diff --git a/.forgejo/workflows/dev-release.yml b/.forgejo/workflows/dev-release.yml new file mode 100644 index 0000000..4161a6c --- /dev/null +++ b/.forgejo/workflows/dev-release.yml @@ -0,0 +1,65 @@ +name: dev release + +# Publishes a dev-track build on every push to `dev` — +# separate from release.yml's tag-triggered stable releases. See +# bread-ecosystem's docs/release-channels.md for the three-track policy +# this is part of. +on: + push: + branches: ['dev'] + +jobs: + build: + runs-on: [self-hosted, hestia] + steps: + - name: checkout + run: | + set -euo pipefail + rm -rf src && mkdir src + git clone --branch dev --depth 1 \ + "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src + + - name: build + run: cd src && cargo build --release --locked + + - name: compute dev version + run: | + set -euo pipefail + cd src + CUR="$(grep -m1 '^version' Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" + IFS='.' read -r MA MI PA <<< "${CUR}" + SHA="$(git rev-parse --short HEAD)" + TS="$(date -u +%Y%m%d%H%M%S)" + echo "VERSION=${MA}.${MI}.$((PA + 1))-dev.${TS}+${SHA}" >> "$GITHUB_ENV" + + - name: prepare artifacts + run: | + set -euo pipefail + PKG_DIR="/srv/breadway-dl/dev/breadclip/${VERSION}" + mkdir -p "${PKG_DIR}" + for bin in breadclip breadclipd; do + cp "src/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 src/contrib/breadclipd.service "${PKG_DIR}/" + cp src/bakery.toml "${PKG_DIR}/bakery.toml" + ln -sfn "${VERSION}" "/srv/breadway-dl/dev/breadclip/latest" + + # No GitHub Release upload — dev, like the other non-stable track, + # is only distributed via dl.breadway.dev/dev/. + - name: regenerate dev index.json + env: + MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }} + run: | + set -euo pipefail + if [ -z "${MINISIGN_SEC_KEY:-}" ]; then + echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate dev index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone on the dev track)" + exit 1 + fi + rm -rf /tmp/bread-ecosystem-ci + # --branch dev: the TRACK-aware gen-index.sh isn't merged to + # bread-ecosystem's main yet. + git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci + TRACK=dev bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh From 06ab15c2c0409fd4552024c8ced4669a16701a6b Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 10:10:20 +0800 Subject: [PATCH 03/23] ci: retrigger dev-track build now that BAKERY_MINISIGN_SEC_KEY_PATH is set From a35cd6f36320ce0f2c5cdfc9482c4158195762cd Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 10:24:49 +0800 Subject: [PATCH 04/23] ci: use a unique temp dir for the bread-ecosystem clone in dev/beta CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fixed /tmp/bread-ecosystem-ci path races when multiple repos' dev/beta workflows run close together on the same self-hosted runner — one job's rm -rf/clone can stomp another's in-progress checkout, causing the regenerate-index step to fail intermittently. Switch to mktemp -d. --- .forgejo/workflows/beta-release.yml | 12 +++++++----- .forgejo/workflows/dev-release.yml | 14 ++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.forgejo/workflows/beta-release.yml b/.forgejo/workflows/beta-release.yml index e1aaac6..08c48a2 100644 --- a/.forgejo/workflows/beta-release.yml +++ b/.forgejo/workflows/beta-release.yml @@ -49,8 +49,10 @@ jobs: echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate beta index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone on the beta track)" exit 1 fi - rm -rf /tmp/bread-ecosystem-ci - # --branch dev: the TRACK-aware gen-index.sh isn't merged to - # bread-ecosystem's main yet. - git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci - TRACK=beta bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh + rm -rf /tmp/bread-ecosystem-ci-* 2>/dev/null || true + # mktemp: a fixed clone path races when multiple repos' dev/beta + # workflows run close together on the same self-hosted runner. + ECOSYSTEM_CI_DIR="$(mktemp -d /tmp/bread-ecosystem-ci-XXXXXX)" + git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}" + TRACK=beta bash "${ECOSYSTEM_CI_DIR}/scripts/gen-index.sh" + rm -rf "${ECOSYSTEM_CI_DIR}" diff --git a/.forgejo/workflows/dev-release.yml b/.forgejo/workflows/dev-release.yml index 4161a6c..d0fb4d2 100644 --- a/.forgejo/workflows/dev-release.yml +++ b/.forgejo/workflows/dev-release.yml @@ -26,7 +26,7 @@ jobs: run: | set -euo pipefail cd src - CUR="$(grep -m1 '^version' Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" + CUR="$(grep -m1 '^version' breadclip/Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" IFS='.' read -r MA MI PA <<< "${CUR}" SHA="$(git rev-parse --short HEAD)" TS="$(date -u +%Y%m%d%H%M%S)" @@ -58,8 +58,10 @@ jobs: echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate dev index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone on the dev track)" exit 1 fi - rm -rf /tmp/bread-ecosystem-ci - # --branch dev: the TRACK-aware gen-index.sh isn't merged to - # bread-ecosystem's main yet. - git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci - TRACK=dev bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh + rm -rf /tmp/bread-ecosystem-ci-* 2>/dev/null || true + # mktemp: a fixed clone path races when multiple repos' dev/beta + # workflows run close together on the same self-hosted runner. + ECOSYSTEM_CI_DIR="$(mktemp -d /tmp/bread-ecosystem-ci-XXXXXX)" + git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}" + TRACK=dev bash "${ECOSYSTEM_CI_DIR}/scripts/gen-index.sh" + rm -rf "${ECOSYSTEM_CI_DIR}" From 58aa415ef066b9a84d96db28a7bad0392033af69 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 11:37:38 +0800 Subject: [PATCH 05/23] random commit message, read it yourself --- .gitignore | 3 +++ Cargo.lock | 27 ++++++++++++++------------- breadclipd/Cargo.toml | 5 +---- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 36f7f5b..8d2803a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ logs/ # Runtime files *.sock *.pid + +# Local hygiene notes (not for commit) +CLAUDE.md diff --git a/Cargo.lock b/Cargo.lock index 8b5c730..e1ab23b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,16 +57,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "bread-utils" -version = "0.3.0" -dependencies = [ - "bread-shared", - "dirs", - "serde", - "serde_json", -] - [[package]] name = "bread-utils" version = "0.3.0" @@ -79,12 +69,23 @@ dependencies = [ "serde_json", ] +[[package]] +name = "bread-utils" +version = "0.3.1" +source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.3.1#157ed6e3782ac2facd8b517d7c247713f36854ed" +dependencies = [ + "bread-shared", + "dirs", + "serde", + "serde_json", +] + [[package]] name = "breadclip" version = "0.2.1" dependencies = [ "bread-theme", - "bread-utils 0.3.0 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0)", + "bread-utils 0.3.0", "breadclip-core", "gtk4", "gtk4-layer-shell", @@ -95,7 +96,7 @@ dependencies = [ name = "breadclip-core" version = "0.2.1" dependencies = [ - "bread-utils 0.3.0 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0)", + "bread-utils 0.3.0", "dirs", "hex", "rusqlite", @@ -107,7 +108,7 @@ dependencies = [ name = "breadclipd" version = "0.2.1" dependencies = [ - "bread-utils 0.3.0", + "bread-utils 0.3.1", "breadclip-core", "serde_json", "tempfile", diff --git a/breadclipd/Cargo.toml b/breadclipd/Cargo.toml index b7158d4..b384b08 100644 --- a/breadclipd/Cargo.toml +++ b/breadclipd/Cargo.toml @@ -9,10 +9,7 @@ path = "src/main.rs" [dependencies] breadclip-core = { path = "../breadclip-core" } -# TODO(owner): switch to a tag-pinned git dependency once bread-shared is -# released/tagged for external consumption, matching the bread-theme pattern — -# see the same stopgap already in place for bread-utils's own dependents. -bread-utils = { path = "../../bread-ecosystem/bread-utils", features = ["bread-client"] } +bread-utils = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.3.1", features = ["bread-client"] } serde_json = "1" [dev-dependencies] From eab00c0712177866d824ba62e4d10072492fdae5 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 13:53:07 +0800 Subject: [PATCH 06/23] ci: base dev version on the latest published tag, not Cargo.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cargo.toml can drift stale relative to the actual last release (observed on breadbox/breadpad/breadcrumbs/breadpaper), which made the auto-bumped dev version sort as OLDER than what's already installed — bakery's semver check correctly refused those "updates". Deriving the base version from git ls-remote --tags instead is self-healing regardless of Cargo.toml drift, with a Cargo.toml fallback only for a repo with no tags yet. --- .forgejo/workflows/dev-release.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/dev-release.yml b/.forgejo/workflows/dev-release.yml index d0fb4d2..2a0a1e9 100644 --- a/.forgejo/workflows/dev-release.yml +++ b/.forgejo/workflows/dev-release.yml @@ -26,7 +26,19 @@ jobs: run: | set -euo pipefail cd src - CUR="$(grep -m1 '^version' breadclip/Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" + # Base the dev version off the latest published stable tag, + # not Cargo.toml — Cargo.toml can go stale relative to the last + # real release (seen in practice: breadbox/breadpad/breadcrumbs/ + # breadpaper), which would make a dev build sort as OLDER than + # what's already installed and bakery would correctly refuse it. + LATEST_TAG="$(git ls-remote --tags --refs \ + "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" 'v*' \ + | awk -F/ '{print $NF}' | sed 's/^v//' | sort -V | tail -1)" + if [ -n "${LATEST_TAG}" ]; then + CUR="${LATEST_TAG}" + else + CUR="$(grep -m1 '^version' breadclip/Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" + fi IFS='.' read -r MA MI PA <<< "${CUR}" SHA="$(git rev-parse --short HEAD)" TS="$(date -u +%Y%m%d%H%M%S)" From 2e999546429d78e958348391daadeea6957d1afe Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 18:37:48 +0800 Subject: [PATCH 07/23] ci: make beta a branch-triggered freeze track, not a one-off tag Beta is now a real stabilization branch: publishes on every push to `beta` (mirroring dev's model, auto-versioned X.Y.Z-beta.+, base version from the latest published tag) instead of a manual beta-v* tag. Fixes made during the freeze land via fix/ branches merged into `beta` directly. The gen-index.sh clone for beta pulls bread-ecosystem's default branch (main) rather than pinning to dev, since beta is the more stable track and main now carries the TRACK-aware script. --- .forgejo/workflows/beta-release.yml | 41 ++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/.forgejo/workflows/beta-release.yml b/.forgejo/workflows/beta-release.yml index 08c48a2..cabc105 100644 --- a/.forgejo/workflows/beta-release.yml +++ b/.forgejo/workflows/beta-release.yml @@ -1,12 +1,12 @@ name: beta release -# Publishes a beta-track build when a `beta-v*` tag is pushed — -# separate from release.yml's tag-triggered stable releases. See -# bread-ecosystem's docs/release-channels.md for the three-track policy -# this is part of. +# Publishes a beta-track build on every push to `beta` — a frozen +# stabilization branch cut from `dev` when ready to stabilize; only +# fix/ branches merged into `beta` should land here afterward. +# See bread-ecosystem's docs/release-channels.md for the three-track policy. on: push: - tags: ['beta-v*'] + branches: ['beta'] jobs: build: @@ -16,16 +16,37 @@ jobs: run: | set -euo pipefail rm -rf src && mkdir src - git clone --branch "${GITHUB_REF_NAME}" --depth 1 \ + git clone --branch beta --depth 1 \ "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build run: cd src && cargo build --release --locked + - name: compute beta version + run: | + set -euo pipefail + cd src + # Base the beta version off the latest published stable tag, + # not Cargo.toml — Cargo.toml can go stale relative to the last + # real release (seen in practice: breadbox/breadpad/breadcrumbs/ + # breadpaper), which would make a beta build sort as OLDER than + # what's already installed and bakery would correctly refuse it. + LATEST_TAG="$(git ls-remote --tags --refs \ + "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" 'v*' \ + | awk -F/ '{print $NF}' | sed 's/^v//' | sort -V | tail -1)" + if [ -n "${LATEST_TAG}" ]; then + CUR="${LATEST_TAG}" + else + CUR="$(grep -m1 '^version' breadclip/Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" + fi + IFS='.' read -r MA MI PA <<< "${CUR}" + SHA="$(git rev-parse --short HEAD)" + TS="$(date -u +%Y%m%d%H%M%S)" + echo "VERSION=${MA}.${MI}.$((PA + 1))-beta.${TS}+${SHA}" >> "$GITHUB_ENV" + - name: prepare artifacts run: | set -euo pipefail - VERSION="${GITHUB_REF_NAME#beta-v}" PKG_DIR="/srv/breadway-dl/beta/breadclip/${VERSION}" mkdir -p "${PKG_DIR}" for bin in breadclip breadclipd; do @@ -38,8 +59,8 @@ jobs: cp src/bakery.toml "${PKG_DIR}/bakery.toml" ln -sfn "${VERSION}" "/srv/breadway-dl/beta/breadclip/latest" - # No GitHub Release upload — beta, like the other non-stable track, - # is only distributed via dl.breadway.dev/beta/. + # No GitHub Release upload — beta, like dev, is only distributed via + # dl.breadway.dev/beta/. - name: regenerate beta index.json env: MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }} @@ -53,6 +74,6 @@ jobs: # mktemp: a fixed clone path races when multiple repos' dev/beta # workflows run close together on the same self-hosted runner. ECOSYSTEM_CI_DIR="$(mktemp -d /tmp/bread-ecosystem-ci-XXXXXX)" - git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}" + git clone https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}" TRACK=beta bash "${ECOSYSTEM_CI_DIR}/scripts/gen-index.sh" rm -rf "${ECOSYSTEM_CI_DIR}" From b95e4c946647232813734d8f30b9fd743003d8cc Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 22 Jul 2026 19:42:03 +0800 Subject: [PATCH 08/23] docs: add CONTRIBUTING.md Documents the dev/beta/main branch and release-track workflow shared across the bread ecosystem. See bread-ecosystem's docs/release-channels.md for the full policy this implements. --- CONTRIBUTING.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..49095a5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,90 @@ +# Contributing + +`breadclip` — Wayland clipboard history manager for Hyprland (daemon + GTK4 popup). + +Part of the bread ecosystem; this repo follows the same branch/release +workflow as every other ecosystem product. + +## Branches + +- **`main`** — release branch, always tag-ready. Nothing is committed to it + directly; it only moves forward via a `beta` merge (see below). +- **`dev`** — integration branch. All day-to-day work lands here first. + Every push to `dev` automatically builds and publishes a **dev-track** + build (see Tracks below) — use this to test your change in a real install + before it goes any further. +- **`beta`** — a frozen stabilization branch, cut from `dev` periodically. + Every push to `beta` automatically builds and publishes a **beta-track** + build. While a freeze is active, only fixes for issues found *in that + freeze* should land on `beta`. + +New work — features and bug fixes alike — goes on a short-lived branch: + +``` +feature/ +fix/ +``` + +Branch off `dev`, open a PR/push back into `dev` when ready. If you're fixing +something reported against an active `beta` freeze, branch off `beta` +instead, merge the fix there to unblock testers, and also forward the same +fix into `dev` so it doesn't quietly reappear next cycle. + +## The release cycle + +1. Work accumulates on `dev` via `feature/x` / `fix/x` branches. Each push + auto-publishes a dev build — install it with `bakery track set dev` and + `bakery update --all`, then report or fix anything broken with another + push to `dev`. +2. Once `dev` has gone roughly **a week** without new issues, `beta` is cut + fresh from `dev`'s current tip. This freezes it as the stabilization + target — `dev` keeps moving independently starting the next cycle. +3. `beta` is open for anyone to test: `bakery track set beta` and + `bakery update --all`. **File issues against anything you find on this + repo's Forgejo issue tracker.** Fixes land via `fix/` branches + merged into `beta`. +4. Once `beta` has gone roughly **a month** without new issues, it's merged + into `main` and tagged `vX.Y.Z` — that tag is what actually triggers the + stable release build. `beta` is then reset from `dev` to start the next + cycle. + +## Tracks, from a user's perspective + +``` +bakery track show # what you're currently on (defaults to stable) +bakery track set dev # or beta, or stable +bakery update --all # pull the latest build on your current track +``` + +| Track | What it is | Published from | +|--------|-----------|-----------------| +| `stable` | The last tagged release | `main`, on a `vX.Y.Z` tag push | +| `beta` | Current stabilization freeze | `beta`, on every push | +| `dev` | Bleeding edge | `dev`, on every push | + +Dev/beta versions are auto-computed (`X.Y.Z-dev.+` / +`-beta.…`) from the latest published stable tag, so they always sort as +newer than what you have installed — no manual version bumping needed when +pushing to `dev` or `beta`. + +## Local development + +```sh +cargo build --release --workspace +cargo test --release --workspace +``` + +## CI + +- `dev-release.yml` — triggered on push to `dev`. +- `beta-release.yml` — triggered on push to `beta`. +- `release.yml` — triggered on a `v*` tag push, cuts the actual stable release. + +All CI runs on a self-hosted runner; nothing runs automatically on plain +commits or PRs beyond the track builds above. See +[bread-ecosystem's docs/release-channels.md](https://git.breadway.dev/Breadway/bread-ecosystem/src/branch/main/docs/release-channels.md) +for the full policy, including how a new product gets wired onto these tracks. + +## Questions + +Open an issue on this repo's Forgejo tracker. From b125facc6791af3e9689bf64154527c16e0a5acb Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 29 Jul 2026 11:37:01 +0800 Subject: [PATCH 09/23] breadclip: add --screenshot CLI mode for automated capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same pattern as breadbar/breadbox: render the clipboard-history panel, capture it via bread-screenshots, then exit. One view ("history"), full known-size canvas capture since the panel isn't its own layer surface. active_window()/focused_monitor() (used to anchor the panel next to the real focused window) both resolve to None inside bread-capture's isolated environment (HYPRLAND_INSTANCE_SIGNATURE is deliberately unset there), so the panel falls back to its already-existing centered layout — exactly the deterministic behavior a screenshot needs, with no screenshot-mode- specific positioning logic required. Also fixes the same singleton footgun as breadbox: toggle_or_kill() kills whatever's holding breadclip's single-instance lock, which is typically the real running instance. A screenshot run now skips it entirely instead of fighting over (and killing) the operator's real clipboard panel. --- Cargo.lock | 205 ++++++++++++++++++++++++++++++++++-- breadclip/Cargo.toml | 5 + breadclip/src/main.rs | 57 +++++++--- breadclip/src/screenshot.rs | 100 ++++++++++++++++++ 4 files changed, 348 insertions(+), 19 deletions(-) create mode 100644 breadclip/src/screenshot.rs diff --git a/Cargo.lock b/Cargo.lock index e1ab23b..925c25d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,62 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + [[package]] name = "autocfg" version = "1.5.1" @@ -35,6 +91,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bread-screenshots" +version = "0.3.1" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev#1a3475bd2358202f60e29c9bd27d06b1428b1a27" +dependencies = [ + "anyhow", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev)", + "tracing", +] + [[package]] name = "bread-shared" version = "0.7.0" @@ -80,13 +146,26 @@ dependencies = [ "serde_json", ] +[[package]] +name = "bread-utils" +version = "0.3.1" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev#1a3475bd2358202f60e29c9bd27d06b1428b1a27" +dependencies = [ + "dirs", + "serde", + "serde_json", +] + [[package]] name = "breadclip" version = "0.2.1" dependencies = [ + "anyhow", + "bread-screenshots", "bread-theme", "bread-utils 0.3.0", "breadclip-core", + "clap", "gtk4", "gtk4-layer-shell", "serde_json", @@ -108,7 +187,7 @@ dependencies = [ name = "breadclipd" version = "0.2.1" dependencies = [ - "bread-utils 0.3.1", + "bread-utils 0.3.1 (git+https://github.com/Breadway/bread-ecosystem?tag=v0.3.1)", "breadclip-core", "serde_json", "tempfile", @@ -163,6 +242,52 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "clap" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -303,7 +428,7 @@ checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -495,7 +620,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -629,7 +754,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -697,6 +822,12 @@ dependencies = [ "hashbrown 0.17.1", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itoa" version = "1.0.18" @@ -768,6 +899,12 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "option-ext" version = "0.2.0" @@ -922,7 +1059,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -985,6 +1122,12 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.119" @@ -996,6 +1139,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "system-deps" version = "7.0.8" @@ -1045,7 +1199,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1140,6 +1294,37 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + [[package]] name = "typenum" version = "1.20.1" @@ -1152,6 +1337,12 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1298,7 +1489,7 @@ checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] diff --git a/breadclip/Cargo.toml b/breadclip/Cargo.toml index 032d7d0..258e60a 100644 --- a/breadclip/Cargo.toml +++ b/breadclip/Cargo.toml @@ -12,6 +12,11 @@ breadclip-core = { path = "../breadclip-core" } bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.3.0", features = ["gtk"] } # (Path corrected: previously pointed at a since-cleaned-up "bread-ecosystem-fix-worktree" checkout that no longer exists on disk.) bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.3.0", features = ["gtk"] } +# Capture primitives for `--screenshot` mode — see src/screenshot.rs. Not +# tag-pinned like the deps above since it doesn't have a tagged release yet. +bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", branch = "dev" } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" serde_json = "1" +clap = { version = "4", features = ["derive"] } +anyhow = "1" diff --git a/breadclip/src/main.rs b/breadclip/src/main.rs index 282b4a6..05b0dc8 100644 --- a/breadclip/src/main.rs +++ b/breadclip/src/main.rs @@ -1,4 +1,5 @@ mod css; +mod screenshot; use breadclip_core::{ClipEntry, HistoryDb}; use bread_theme::{load_palette}; @@ -184,10 +185,17 @@ fn do_copy(entry: &ClipEntry) { // ---- UI --------------------------------------------------------------------- -fn run_ui(entries: Vec) { - let app = Application::builder() - .application_id("com.breadway.breadclip") - .build(); +fn run_ui(entries: Vec, screenshot_req: Option) { + let mut builder = Application::builder().application_id("com.breadway.breadclip"); + if screenshot_req.is_some() { + // GApplication is single-instance by default; this machine typically + // already has a real breadclip instance, so without this a + // screenshot run would just message the *existing* instance instead + // of starting a fresh one that ever sees `screenshot_req`. + builder = builder.flags(gtk4::gio::ApplicationFlags::NON_UNIQUE); + } + let app = builder.build(); + let is_screenshot_run = screenshot_req.is_some(); app.connect_activate(move |app| { bread_theme::gtk::apply_shared(); @@ -408,25 +416,50 @@ fn run_ui(entries: Vec) { bread_utils::gtk_popup::close_on_outside_click(&window, &panel, move || close_outside()); } + if let Some(req) = screenshot_req.clone() { + screenshot::dispatch(&window, req); + } + window.present(); search.grab_focus(); }); - app.run(); + if is_screenshot_run { + // GLib's own option parser otherwise rejects --screenshot/--output + // before clap ever sees them (`Cli::parse()` already ran in `main`, + // over the real argv). + app.run_with_args(&[] as &[&str]); + } else { + app.run(); + } } // ---- Main ------------------------------------------------------------------- fn main() { + use clap::Parser; + let cli = screenshot::Cli::parse(); + let screenshot_req = cli.screenshot_request(); + + // `toggle_or_kill` kills whatever's holding the single-instance lock — + // a real, already-running breadclip 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 clipboard panel. + // // 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 = match bread_utils::singleton::toggle_or_kill("breadclip") { - Ok(bread_utils::singleton::Toggle::Started(guard)) => Some(guard), - Ok(bread_utils::singleton::Toggle::KilledExisting) => return, - Err(e) => { - eprintln!("breadclip: single-instance lock unavailable ({e}); continuing without it"); - None + let _singleton_guard = if screenshot_req.is_some() { + None + } else { + match bread_utils::singleton::toggle_or_kill("breadclip") { + Ok(bread_utils::singleton::Toggle::Started(guard)) => Some(guard), + Ok(bread_utils::singleton::Toggle::KilledExisting) => return, + Err(e) => { + eprintln!("breadclip: single-instance lock unavailable ({e}); continuing without it"); + None + } } }; @@ -434,5 +467,5 @@ fn main() { .and_then(|db| db.list_entries(MAX_ENTRIES)) .unwrap_or_default(); - run_ui(entries); + run_ui(entries, screenshot_req); } diff --git a/breadclip/src/screenshot.rs b/breadclip/src/screenshot.rs new file mode 100644 index 0000000..b4fd8fa --- /dev/null +++ b/breadclip/src/screenshot.rs @@ -0,0 +1,100 @@ +//! `--screenshot` CLI mode: render breadclip's clipboard-history panel, +//! capture it via `bread-screenshots`, then exit — driven by +//! `bread-ecosystem`'s `bread-capture` orchestrator, or run standalone for +//! one-off captures. +//! +//! breadclip has one view worth capturing: the history panel itself. Like +//! breadbox, it's a `halign`/`valign`-positioned panel over a full-screen +//! transparent overlay rather than its own layer surface, so a full +//! known-size canvas capture is the simplest reliable option. In screenshot +//! mode there's never a focused window to anchor the panel next to (see +//! `bread_utils::hypr::active_window` in `main.rs`, which the isolated +//! capture environment always reports `None` for — deliberately, see +//! `bread-capture`'s isolation module), so the panel always falls back to +//! centered, which is exactly what we want for a consistent screenshot. + +use clap::Parser; +use gtk4::prelude::*; +use std::path::PathBuf; +use std::time::Duration; + +/// Extra settle time after `map` for the first frame to actually paint +/// before grim runs — `map` fires once the surface exists, not once +/// anything has been drawn into it. +const SETTLE_DELAY: Duration = Duration::from_millis(300); + +#[derive(Parser)] +#[command(name = "breadclip")] +pub struct Cli { + /// Render the named view, capture it, then exit instead of running + /// normally. Known views: "history". + #[arg(long)] + pub screenshot: Option, + + /// PNG path to write the capture to. Required together with --screenshot. + #[arg(long)] + pub output: Option, + + /// Capture canvas width — matches the isolated compositor's output width + /// (`bread-capture --isolate-width`). + #[arg(long, default_value_t = 1920)] + pub width: u32, + + /// Capture canvas height — see `width`. + #[arg(long, default_value_t = 1080)] + pub height: u32, +} + +#[derive(Clone)] +pub struct ScreenshotRequest { + pub view: String, + pub output: PathBuf, + pub width: u32, + pub height: u32, +} + +impl Cli { + /// `None` for a normal run. Exits the process with an error if + /// `--screenshot` was given without `--output`, before any GTK setup + /// happens. + pub fn screenshot_request(&self) -> Option { + let view = self.screenshot.clone()?; + let Some(output) = self.output.clone() else { + eprintln!("breadclip: --screenshot requires --output"); + std::process::exit(1); + }; + Some(ScreenshotRequest { view, output, width: self.width, height: self.height }) + } +} + +/// Wire up the given view's screenshot sequence against an already-built, +/// not-yet-presented window. Every path here ends by exiting the process — +/// it never returns control to the normal history-panel UI. +pub fn dispatch(window: >k4::ApplicationWindow, req: ScreenshotRequest) { + match req.view.as_str() { + "history" => { + let output = req.output; + let (width, height) = (req.width as i32, req.height as i32); + window.connect_map(move |_| { + let output = output.clone(); + gtk4::glib::timeout_add_local_once(SETTLE_DELAY, move || { + finish(bread_screenshots::capture_region(0, 0, width, height, &output)); + }); + }); + } + other => { + eprintln!("breadclip: unknown screenshot view '{other}' (known: history)"); + std::process::exit(1); + } + } +} + +fn finish(result: anyhow::Result<()>) { + match result { + Ok(()) => std::process::exit(0), + Err(e) => { + eprintln!("breadclip: screenshot capture failed: {e}"); + std::process::exit(1); + } + } +} From 5a4453c767d84299af6455bb7a718d3dffdc9e60 Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 31 Jul 2026 11:05:29 +0800 Subject: [PATCH 10/23] =?UTF-8?q?CI:=20single-trunk=20model=20=E2=80=94=20?= =?UTF-8?q?dev=20triggers=20on=20main,=20beta=20becomes=20RC-tag-triggered?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the dev/beta branch split with one trunk (main): dev-track builds still publish on every push, but the beta track now publishes from a vX.Y.Z-rc.N prerelease tag instead of a separately-maintained beta branch. Removes the branch nobody reliably kept in sync. --- .forgejo/workflows/dev-release.yml | 15 ++++---- .../{beta-release.yml => rc-release.yml} | 38 +++++-------------- .forgejo/workflows/release.yml | 1 + 3 files changed, 17 insertions(+), 37 deletions(-) rename .forgejo/workflows/{beta-release.yml => rc-release.yml} (58%) diff --git a/.forgejo/workflows/dev-release.yml b/.forgejo/workflows/dev-release.yml index 2a0a1e9..fd4f4df 100644 --- a/.forgejo/workflows/dev-release.yml +++ b/.forgejo/workflows/dev-release.yml @@ -1,12 +1,11 @@ name: dev release -# Publishes a dev-track build on every push to `dev` — -# separate from release.yml's tag-triggered stable releases. See -# bread-ecosystem's docs/release-channels.md for the three-track policy -# this is part of. +# Publishes a dev-track build on every push to `main` (the trunk +# branch — there is no separate `dev` branch). See bread-ecosystem's +# docs/release-channels.md for the release-track policy this is part of. on: push: - branches: ['dev'] + branches: ['main'] jobs: build: @@ -16,7 +15,7 @@ jobs: run: | set -euo pipefail rm -rf src && mkdir src - git clone --branch dev --depth 1 \ + git clone --branch main --depth 1 \ "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build @@ -33,7 +32,7 @@ jobs: # what's already installed and bakery would correctly refuse it. LATEST_TAG="$(git ls-remote --tags --refs \ "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" 'v*' \ - | awk -F/ '{print $NF}' | sed 's/^v//' | sort -V | tail -1)" + | awk -F/ '{print $NF}' | sed 's/^v//' | (grep -v -- '-' || true) | sort -V | tail -1)" if [ -n "${LATEST_TAG}" ]; then CUR="${LATEST_TAG}" else @@ -74,6 +73,6 @@ jobs: # mktemp: a fixed clone path races when multiple repos' dev/beta # workflows run close together on the same self-hosted runner. ECOSYSTEM_CI_DIR="$(mktemp -d /tmp/bread-ecosystem-ci-XXXXXX)" - git clone --branch dev https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}" + git clone --branch main https://git.breadway.dev/Breadway/bread-ecosystem.git "${ECOSYSTEM_CI_DIR}" TRACK=dev bash "${ECOSYSTEM_CI_DIR}/scripts/gen-index.sh" rm -rf "${ECOSYSTEM_CI_DIR}" diff --git a/.forgejo/workflows/beta-release.yml b/.forgejo/workflows/rc-release.yml similarity index 58% rename from .forgejo/workflows/beta-release.yml rename to .forgejo/workflows/rc-release.yml index cabc105..d697a76 100644 --- a/.forgejo/workflows/beta-release.yml +++ b/.forgejo/workflows/rc-release.yml @@ -1,52 +1,32 @@ -name: beta release +name: beta (rc) release -# Publishes a beta-track build on every push to `beta` — a frozen -# stabilization branch cut from `dev` when ready to stabilize; only -# fix/ branches merged into `beta` should land here afterward. -# See bread-ecosystem's docs/release-channels.md for the three-track policy. +# Publishes a beta-track build for any `vX.Y.Z-rc.N` prerelease tag +# pushed to `main` — there is no separate `beta` branch; "freezing" is +# just pausing pushes to main while an RC gets tested. See +# bread-ecosystem's docs/release-channels.md for the release-track policy. on: push: - branches: ['beta'] + tags: ['v*'] jobs: build: + if: ${{ contains(github.ref_name, '-rc.') }} runs-on: [self-hosted, hestia] steps: - name: checkout run: | set -euo pipefail rm -rf src && mkdir src - git clone --branch beta --depth 1 \ + git clone --branch "${GITHUB_REF_NAME}" --depth 1 \ "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build run: cd src && cargo build --release --locked - - name: compute beta version - run: | - set -euo pipefail - cd src - # Base the beta version off the latest published stable tag, - # not Cargo.toml — Cargo.toml can go stale relative to the last - # real release (seen in practice: breadbox/breadpad/breadcrumbs/ - # breadpaper), which would make a beta build sort as OLDER than - # what's already installed and bakery would correctly refuse it. - LATEST_TAG="$(git ls-remote --tags --refs \ - "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" 'v*' \ - | awk -F/ '{print $NF}' | sed 's/^v//' | sort -V | tail -1)" - if [ -n "${LATEST_TAG}" ]; then - CUR="${LATEST_TAG}" - else - CUR="$(grep -m1 '^version' breadclip/Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" - fi - IFS='.' read -r MA MI PA <<< "${CUR}" - SHA="$(git rev-parse --short HEAD)" - TS="$(date -u +%Y%m%d%H%M%S)" - echo "VERSION=${MA}.${MI}.$((PA + 1))-beta.${TS}+${SHA}" >> "$GITHUB_ENV" - - name: prepare artifacts run: | set -euo pipefail + VERSION="${GITHUB_REF_NAME#v}" PKG_DIR="/srv/breadway-dl/beta/breadclip/${VERSION}" mkdir -p "${PKG_DIR}" for bin in breadclip breadclipd; do diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index 3451346..803be86 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -6,6 +6,7 @@ on: jobs: build: + if: ${{ !contains(github.ref_name, '-rc.') }} runs-on: [self-hosted, hestia] steps: - name: checkout From 2c03e73da587175a30f53d0248f38783dc10372f Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 31 Jul 2026 11:08:41 +0800 Subject: [PATCH 11/23] CONTRIBUTING.md: document single-trunk + RC-tag release model --- CONTRIBUTING.md | 70 ++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49095a5..98faf13 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,16 +7,10 @@ workflow as every other ecosystem product. ## Branches -- **`main`** — release branch, always tag-ready. Nothing is committed to it - directly; it only moves forward via a `beta` merge (see below). -- **`dev`** — integration branch. All day-to-day work lands here first. - Every push to `dev` automatically builds and publishes a **dev-track** - build (see Tracks below) — use this to test your change in a real install - before it goes any further. -- **`beta`** — a frozen stabilization branch, cut from `dev` periodically. - Every push to `beta` automatically builds and publishes a **beta-track** - build. While a freeze is active, only fixes for issues found *in that - freeze* should land on `beta`. +There is one long-lived branch: **`main`**. All day-to-day work lands here. +Every push to `main` automatically builds and publishes a **dev-track** +build (see Tracks below) — a real install you can test before cutting +anything more formal. New work — features and bug fixes alike — goes on a short-lived branch: @@ -25,28 +19,26 @@ feature/ fix/ ``` -Branch off `dev`, open a PR/push back into `dev` when ready. If you're fixing -something reported against an active `beta` freeze, branch off `beta` -instead, merge the fix there to unblock testers, and also forward the same -fix into `dev` so it doesn't quietly reappear next cycle. +Branch off `main`, open a PR/push back into `main` when ready. Short-lived +branches get deleted on merge — they never accumulate the kind of drift a +second long-lived branch does. ## The release cycle -1. Work accumulates on `dev` via `feature/x` / `fix/x` branches. Each push +There's no separate `beta` or release branch — "stable" and "beta" are both +just **tags** on `main`, not branches that need to be kept in sync: + +1. Work accumulates on `main` via `feature/x` / `fix/x` branches. Each push auto-publishes a dev build — install it with `bakery track set dev` and - `bakery update --all`, then report or fix anything broken with another - push to `dev`. -2. Once `dev` has gone roughly **a week** without new issues, `beta` is cut - fresh from `dev`'s current tip. This freezes it as the stabilization - target — `dev` keeps moving independently starting the next cycle. -3. `beta` is open for anyone to test: `bakery track set beta` and - `bakery update --all`. **File issues against anything you find on this - repo's Forgejo issue tracker.** Fixes land via `fix/` branches - merged into `beta`. -4. Once `beta` has gone roughly **a month** without new issues, it's merged - into `main` and tagged `vX.Y.Z` — that tag is what actually triggers the - stable release build. `beta` is then reset from `dev` to start the next - cycle. + `bakery update --all`, then fix anything broken with another push. +2. When you want to stabilize before a real release, tag a release + candidate: `git tag vX.Y.Z-rc.1 && git push origin vX.Y.Z-rc.1` (push to + both remotes). That tag alone triggers a beta-track build — + "freezing" is just pausing pushes to `main` while you test it, not a + branch operation. Cut `-rc.2`, `-rc.3`, etc. for further fixes. +3. Once an RC has gone without issues, tag the real release: + `git tag vX.Y.Z && git push origin vX.Y.Z` — that's what triggers the + signed stable release build. ## Tracks, from a user's perspective @@ -58,14 +50,15 @@ bakery update --all # pull the latest build on your current track | Track | What it is | Published from | |--------|-----------|-----------------| -| `stable` | The last tagged release | `main`, on a `vX.Y.Z` tag push | -| `beta` | Current stabilization freeze | `beta`, on every push | -| `dev` | Bleeding edge | `dev`, on every push | +| `stable` | The last tagged release | a `vX.Y.Z` tag | +| `beta` | Latest release candidate | a `vX.Y.Z-rc.N` tag | +| `dev` | Bleeding edge | `main`, on every push | -Dev/beta versions are auto-computed (`X.Y.Z-dev.+` / -`-beta.…`) from the latest published stable tag, so they always sort as -newer than what you have installed — no manual version bumping needed when -pushing to `dev` or `beta`. +Dev versions are auto-computed (`X.Y.Z-dev.+`) from the +latest published stable tag, so they always sort as newer than what you +have installed — no manual version bumping needed. Beta versions are just +the RC tag itself (already valid semver, already sorts below the real +release it's a candidate for). ## Local development @@ -76,9 +69,10 @@ cargo test --release --workspace ## CI -- `dev-release.yml` — triggered on push to `dev`. -- `beta-release.yml` — triggered on push to `beta`. -- `release.yml` — triggered on a `v*` tag push, cuts the actual stable release. +- `dev-release.yml` — triggered on push to `main`. +- `rc-release.yml` — triggered on any `vX.Y.Z-rc.N` tag push. +- `release.yml` — triggered on any other `v*` tag push, cuts the actual + stable release. All CI runs on a self-hosted runner; nothing runs automatically on plain commits or PRs beyond the track builds above. See From 555ae5b0d1b332fb2108eeacb0e9f8c338ae4f7e Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 31 Jul 2026 14:30:01 +0800 Subject: [PATCH 12/23] Repoint bread-ecosystem git deps from dev to main branch --- Cargo.lock | 6 +++--- breadclip/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 925c25d..fbda1c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,10 +94,10 @@ dependencies = [ [[package]] name = "bread-screenshots" version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev#1a3475bd2358202f60e29c9bd27d06b1428b1a27" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=main#f86e299f4a0ea73ff485cd84923b986ddcc8242e" dependencies = [ "anyhow", - "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev)", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=main)", "tracing", ] @@ -149,7 +149,7 @@ dependencies = [ [[package]] name = "bread-utils" version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=dev#1a3475bd2358202f60e29c9bd27d06b1428b1a27" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=main#f86e299f4a0ea73ff485cd84923b986ddcc8242e" dependencies = [ "dirs", "serde", diff --git a/breadclip/Cargo.toml b/breadclip/Cargo.toml index 258e60a..17edafd 100644 --- a/breadclip/Cargo.toml +++ b/breadclip/Cargo.toml @@ -14,7 +14,7 @@ bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.3.0", features = ["gtk"] } # Capture primitives for `--screenshot` mode — see src/screenshot.rs. Not # tag-pinned like the deps above since it doesn't have a tagged release yet. -bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", branch = "dev" } +bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", branch = "main" } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" serde_json = "1" From 1203102fe98b1a0f9cff7484526ef34196ece364 Mon Sep 17 00:00:00 2001 From: Breadway Date: Wed, 5 Aug 2026 14:03:05 +0800 Subject: [PATCH 13/23] ci: build against bread-ecosystem's shared Arch CI image, add check.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same fix as breadpad: build inside the shared pinned Arch container (bread-ecosystem/ci/, cloned at the sha in ci/bread-ecosystem.rev) instead of building natively against whatever's on the runner host. Adds check.yml (clippy + test on feature/**/fix/**) as a fast-fail gate before anything reaches main. Verified locally: build, clippy, and test all pass through the new container path — no pre-existing lint/test debt found here. --- .forgejo/workflows/check.yml | 24 ++++++++++++++++++++++++ .forgejo/workflows/dev-release.yml | 2 +- .forgejo/workflows/rc-release.yml | 2 +- .forgejo/workflows/release.yml | 2 +- ci/bread-ecosystem.rev | 1 + ci/build.sh | 20 ++++++++++++++++++++ 6 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 .forgejo/workflows/check.yml create mode 100644 ci/bread-ecosystem.rev create mode 100755 ci/build.sh diff --git a/.forgejo/workflows/check.yml b/.forgejo/workflows/check.yml new file mode 100644 index 0000000..b547c34 --- /dev/null +++ b/.forgejo/workflows/check.yml @@ -0,0 +1,24 @@ +name: check + +# Fast-fail lint/test on short-lived work branches, before it ever reaches +# main and triggers a dev-track release build. +on: + push: + branches: ['feature/**', 'fix/**'] + +jobs: + check: + runs-on: [self-hosted, hestia] + steps: + - name: checkout + run: | + set -euo pipefail + rm -rf src && mkdir src + git clone --branch "${GITHUB_REF_NAME}" --depth 1 \ + "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src + + - name: clippy + run: cd src && bash ci/build.sh cargo clippy --workspace --all-targets --locked -- -D warnings + + - name: test + run: cd src && bash ci/build.sh cargo test --workspace --locked diff --git a/.forgejo/workflows/dev-release.yml b/.forgejo/workflows/dev-release.yml index fd4f4df..04b6a68 100644 --- a/.forgejo/workflows/dev-release.yml +++ b/.forgejo/workflows/dev-release.yml @@ -19,7 +19,7 @@ jobs: "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build - run: cd src && cargo build --release --locked + run: cd src && bash ci/build.sh cargo build --release --locked - name: compute dev version run: | diff --git a/.forgejo/workflows/rc-release.yml b/.forgejo/workflows/rc-release.yml index d697a76..46c2343 100644 --- a/.forgejo/workflows/rc-release.yml +++ b/.forgejo/workflows/rc-release.yml @@ -21,7 +21,7 @@ jobs: "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build - run: cd src && cargo build --release --locked + run: cd src && bash ci/build.sh cargo build --release --locked - name: prepare artifacts run: | diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index 803be86..cd68a3a 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -17,7 +17,7 @@ jobs: "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build - run: cd src && cargo build --release --locked + run: cd src && bash ci/build.sh cargo build --release --locked - name: prepare artifacts run: | diff --git a/ci/bread-ecosystem.rev b/ci/bread-ecosystem.rev new file mode 100644 index 0000000..474f1fd --- /dev/null +++ b/ci/bread-ecosystem.rev @@ -0,0 +1 @@ +620c5a1317a6b57276eabca961facdb78bf510db diff --git a/ci/build.sh b/ci/build.sh new file mode 100755 index 0000000..54695da --- /dev/null +++ b/ci/build.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Delegates to bread-ecosystem's shared CI build image/script, pinned to +# the commit in ci/bread-ecosystem.rev — not `main`. bread-ecosystem's CI +# files now affect every product's release pipeline, so bumping the pin +# is a deliberate act instead of silent drift. +# +# Usage: ci/build.sh cargo build --release --locked +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +REV="$(cat "${ROOT}/ci/bread-ecosystem.rev")" + +CACHE_DIR="/tmp/bread-ecosystem-ci-${REV}" +if [ ! -d "$CACHE_DIR" ]; then + rm -rf /tmp/bread-ecosystem-ci-* + git clone https://git.breadway.dev/Breadway/bread-ecosystem.git "$CACHE_DIR" + git -C "$CACHE_DIR" checkout --quiet "$REV" +fi + +bash "${CACHE_DIR}/ci/build.sh" breadclip "$ROOT" "$@" From 8456424f1ab77cc0179e898ad0eb2997cc1e20de Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 21:38:49 +0800 Subject: [PATCH 14/23] Pin bread-theme/utils to bread-ecosystem v0.7.1; add single-trunk CLAUDE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Popup, daemon, and core were split across git.breadway.dev v0.3.0 and GitHub v0.3.1. Point every bread-theme / bread-utils dep at the same Forgejo tag. bread-screenshots is not in v0.7.1 (the crate landed after that tag) so it stays on that remote's main until the next ecosystem release. CLAUDE.md now documents the single-trunk model. EVENTS.md is unchanged — no pin/select verbs. --- .gitignore | 3 -- CLAUDE.md | 62 +++++++++++++++++++++++++++++++++++++++ Cargo.lock | 26 +++++----------- breadclip-core/Cargo.toml | 3 +- breadclip/Cargo.toml | 10 +++---- breadclipd/Cargo.toml | 2 +- 6 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index 8d2803a..36f7f5b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,3 @@ logs/ # Runtime files *.sock *.pid - -# Local hygiene notes (not for commit) -CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..cb27ea1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,62 @@ +# CLAUDE.md — Repo hygiene + +Scope: this file covers *repo hygiene* — branching, remotes, CI — plus a +short map of the binaries. It is not user-facing project documentation. + +This repo follows the branch/release workflow documented in `CONTRIBUTING.md` +— read and follow it for any git, branch, or release work here (the +single-trunk model, `feature/x`/`fix/x` branch naming, how RC tags work, +etc). Don't improvise a different workflow. The short version: there is one +long-lived branch, `main` — no `dev` or `beta` branch exists. `main` +auto-publishes a dev-track build on every push. "Beta" and "stable" are both +just tags, not branches: push a `vX.Y.Z-rc.N` tag to publish a beta-track +build, push a plain `vX.Y.Z` tag to cut the signed stable release. +"Freezing" for stabilization means pausing pushes to `main`, not moving a +branch. This replaced an earlier three-branch (`dev`/`beta`/`main`) model +after `main` was found to have silently rotted out of sync with `dev`/`beta` +across most repos in this ecosystem. + +When starting work on a new feature, create branch `feature/`. +When working on a bug or issue, create branch `fix/`. + +## Remotes +- `origin` — Forgejo (`git.breadway.dev` via Hestia, SSH) — authoritative. +- `github` — GitHub mirror. Push `origin` only; GitHub auto-mirrors. + +## CI +- `check.yml` — clippy + test, triggers on push to `feature/**`/`fix/**`. +- `dev-release.yml` — triggers on push to `main`. +- `rc-release.yml` — triggers on `vX.Y.Z-rc.N` tag push. +- `release.yml` — triggers on any other `v*` tag push. + +All four run on a self-hosted runner (`hestia`) inside a pinned Arch +container — not the host's native environment. The Containerfile/build +script are shared across bread-ecosystem products and live in +`bread-ecosystem/ci/`; this repo's `ci/build.sh` clones that repo at the +sha in `ci/bread-ecosystem.rev` (deliberately pinned, not `main`) and +delegates to it. Nothing runs automatically on plain commits or PRs +beyond what's listed. + +## Architecture + +Three crates: + +| Crate | Role | +|---|---| +| `breadclipd` | Clipboard-watch daemon; persists history to SQLite | +| `breadclip` | GTK4 Layer Shell popup (thin UI over the same DB) | +| `breadclip-core` | Shared history schema / DB access | + +`--screenshot` (`breadclip/src/screenshot.rs`) captures the history panel +through `bread-screenshots`; do not rewrite it just to retarget the crate pin. + +`EVENTS.md` is the bread-event contract. App id `clip`. Implemented: +`bread.clip.copied`, `bread.clip.clear.done`/`.failed`, and command +`bread.command.clip.clear`. There is no pin/select — the history schema has +no pinned column and the popup has no pin UI. Do not invent +`bread.command.clip.pin`/`.select` (or matching events) ahead of a real +product feature. + +## Don't +- Don't embed credentials in remote URLs — SSH or a credential helper only. +- Don't invent pin/select on the event bus. See `EVENTS.md`. diff --git a/Cargo.lock b/Cargo.lock index fbda1c7..7b6ca6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,8 +114,8 @@ dependencies = [ [[package]] name = "bread-theme" -version = "0.3.0" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0#8e82d2d833e992ce939a5b836f910ee109f2e939" +version = "0.3.1" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" dependencies = [ "dirs", "gtk4", @@ -123,25 +123,15 @@ dependencies = [ "serde_json", ] -[[package]] -name = "bread-utils" -version = "0.3.0" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0#8e82d2d833e992ce939a5b836f910ee109f2e939" -dependencies = [ - "dirs", - "gtk4", - "gtk4-layer-shell", - "serde", - "serde_json", -] - [[package]] name = "bread-utils" version = "0.3.1" -source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.3.1#157ed6e3782ac2facd8b517d7c247713f36854ed" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" dependencies = [ "bread-shared", "dirs", + "gtk4", + "gtk4-layer-shell", "serde", "serde_json", ] @@ -163,7 +153,7 @@ dependencies = [ "anyhow", "bread-screenshots", "bread-theme", - "bread-utils 0.3.0", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", "breadclip-core", "clap", "gtk4", @@ -175,7 +165,7 @@ dependencies = [ name = "breadclip-core" version = "0.2.1" dependencies = [ - "bread-utils 0.3.0", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", "dirs", "hex", "rusqlite", @@ -187,7 +177,7 @@ dependencies = [ name = "breadclipd" version = "0.2.1" dependencies = [ - "bread-utils 0.3.1 (git+https://github.com/Breadway/bread-ecosystem?tag=v0.3.1)", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", "breadclip-core", "serde_json", "tempfile", diff --git a/breadclip-core/Cargo.toml b/breadclip-core/Cargo.toml index fabdf67..c57a76c 100644 --- a/breadclip-core/Cargo.toml +++ b/breadclip-core/Cargo.toml @@ -8,8 +8,7 @@ rusqlite = { version = "0.31", features = ["bundled"] } sha2 = "0.10" hex = "0.4" dirs = "5" -# (Path corrected: previously pointed at a since-cleaned-up "bread-ecosystem-fix-worktree" checkout that no longer exists on disk.) -bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.3.0" } +bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1" } [dev-dependencies] tempfile = "3" diff --git a/breadclip/Cargo.toml b/breadclip/Cargo.toml index 17edafd..1a7ef05 100644 --- a/breadclip/Cargo.toml +++ b/breadclip/Cargo.toml @@ -9,11 +9,11 @@ path = "src/main.rs" [dependencies] breadclip-core = { path = "../breadclip-core" } -bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.3.0", features = ["gtk"] } -# (Path corrected: previously pointed at a since-cleaned-up "bread-ecosystem-fix-worktree" checkout that no longer exists on disk.) -bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.3.0", features = ["gtk"] } -# Capture primitives for `--screenshot` mode — see src/screenshot.rs. Not -# tag-pinned like the deps above since it doesn't have a tagged release yet. +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", features = ["gtk"] } +# Capture primitives for `--screenshot` mode — see src/screenshot.rs. +# Not in bread-ecosystem v0.7.1 (crate landed after that tag); same remote +# as the tag-pinned deps, floating on main until the next ecosystem release. bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", branch = "main" } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" diff --git a/breadclipd/Cargo.toml b/breadclipd/Cargo.toml index b384b08..8e4a9b4 100644 --- a/breadclipd/Cargo.toml +++ b/breadclipd/Cargo.toml @@ -9,7 +9,7 @@ path = "src/main.rs" [dependencies] breadclip-core = { path = "../breadclip-core" } -bread-utils = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.3.1", features = ["bread-client"] } +bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["bread-client"] } serde_json = "1" [dev-dependencies] From e66afd339a3221c769a75fcfda6773448c965971 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 22:03:30 +0800 Subject: [PATCH 15/23] Pin bread-screenshots to the shared ecosystem rev; track AGENTS.md Stop floating on branch=main. Same 69ce2d67 rev as sibling apps (v0.7.1 predates the crate). --- AGENTS.md | 62 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.lock | 6 ++--- breadclip/Cargo.toml | 3 ++- 3 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..dc7aab5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,62 @@ +# AGENTS.md — Repo hygiene + +Scope: this file covers *repo hygiene* — branching, remotes, CI — plus a +short map of the binaries. It is not user-facing project documentation. + +This repo follows the branch/release workflow documented in `CONTRIBUTING.md` +— read and follow it for any git, branch, or release work here (the +single-trunk model, `feature/x`/`fix/x` branch naming, how RC tags work, +etc). Don't improvise a different workflow. The short version: there is one +long-lived branch, `main` — no `dev` or `beta` branch exists. `main` +auto-publishes a dev-track build on every push. "Beta" and "stable" are both +just tags, not branches: push a `vX.Y.Z-rc.N` tag to publish a beta-track +build, push a plain `vX.Y.Z` tag to cut the signed stable release. +"Freezing" for stabilization means pausing pushes to `main`, not moving a +branch. This replaced an earlier three-branch (`dev`/`beta`/`main`) model +after `main` was found to have silently rotted out of sync with `dev`/`beta` +across most repos in this ecosystem. + +When starting work on a new feature, create branch `feature/`. +When working on a bug or issue, create branch `fix/`. + +## Remotes +- `origin` — Forgejo (`git.breadway.dev` via Hestia, SSH) — authoritative. +- `github` — GitHub mirror. Push `origin` only; GitHub auto-mirrors. + +## CI +- `check.yml` — clippy + test, triggers on push to `feature/**`/`fix/**`. +- `dev-release.yml` — triggers on push to `main`. +- `rc-release.yml` — triggers on `vX.Y.Z-rc.N` tag push. +- `release.yml` — triggers on any other `v*` tag push. + +All four run on a self-hosted runner (`hestia`) inside a pinned Arch +container — not the host's native environment. The Containerfile/build +script are shared across bread-ecosystem products and live in +`bread-ecosystem/ci/`; this repo's `ci/build.sh` clones that repo at the +sha in `ci/bread-ecosystem.rev` (deliberately pinned, not `main`) and +delegates to it. Nothing runs automatically on plain commits or PRs +beyond what's listed. + +## Architecture + +Three crates: + +| Crate | Role | +|---|---| +| `breadclipd` | Clipboard-watch daemon; persists history to SQLite | +| `breadclip` | GTK4 Layer Shell popup (thin UI over the same DB) | +| `breadclip-core` | Shared history schema / DB access | + +`--screenshot` (`breadclip/src/screenshot.rs`) captures the history panel +through `bread-screenshots`; do not rewrite it just to retarget the crate pin. + +`EVENTS.md` is the bread-event contract. App id `clip`. Implemented: +`bread.clip.copied`, `bread.clip.clear.done`/`.failed`, and command +`bread.command.clip.clear`. There is no pin/select — the history schema has +no pinned column and the popup has no pin UI. Do not invent +`bread.command.clip.pin`/`.select` (or matching events) ahead of a real +product feature. + +## Don't +- Don't embed credentials in remote URLs — SSH or a credential helper only. +- Don't invent pin/select on the event bus. See `EVENTS.md`. diff --git a/Cargo.lock b/Cargo.lock index 7b6ca6b..f678d95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,10 +94,10 @@ dependencies = [ [[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 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?branch=main)", + "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d)", "tracing", ] @@ -139,7 +139,7 @@ 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?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d#69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" dependencies = [ "dirs", "serde", diff --git a/breadclip/Cargo.toml b/breadclip/Cargo.toml index 1a7ef05..ef0b350 100644 --- a/breadclip/Cargo.toml +++ b/breadclip/Cargo.toml @@ -14,7 +14,8 @@ bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = # Capture primitives for `--screenshot` mode — see src/screenshot.rs. # Not in bread-ecosystem v0.7.1 (crate landed after that tag); same remote # as the tag-pinned deps, floating on main until the next ecosystem release. -bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", branch = "main" } +# v0.7.1 predates this crate; rev-pin so this is not `branch = "main"`. +bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", rev = "69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" serde_json = "1" From e98e37dbf10a97c2fe62ad79bc5adb142cd26493 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 22:04:00 +0800 Subject: [PATCH 16/23] Remove CLAUDE.md (renamed to AGENTS.md) --- CLAUDE.md | 62 ------------------------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index cb27ea1..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,62 +0,0 @@ -# CLAUDE.md — Repo hygiene - -Scope: this file covers *repo hygiene* — branching, remotes, CI — plus a -short map of the binaries. It is not user-facing project documentation. - -This repo follows the branch/release workflow documented in `CONTRIBUTING.md` -— read and follow it for any git, branch, or release work here (the -single-trunk model, `feature/x`/`fix/x` branch naming, how RC tags work, -etc). Don't improvise a different workflow. The short version: there is one -long-lived branch, `main` — no `dev` or `beta` branch exists. `main` -auto-publishes a dev-track build on every push. "Beta" and "stable" are both -just tags, not branches: push a `vX.Y.Z-rc.N` tag to publish a beta-track -build, push a plain `vX.Y.Z` tag to cut the signed stable release. -"Freezing" for stabilization means pausing pushes to `main`, not moving a -branch. This replaced an earlier three-branch (`dev`/`beta`/`main`) model -after `main` was found to have silently rotted out of sync with `dev`/`beta` -across most repos in this ecosystem. - -When starting work on a new feature, create branch `feature/`. -When working on a bug or issue, create branch `fix/`. - -## Remotes -- `origin` — Forgejo (`git.breadway.dev` via Hestia, SSH) — authoritative. -- `github` — GitHub mirror. Push `origin` only; GitHub auto-mirrors. - -## CI -- `check.yml` — clippy + test, triggers on push to `feature/**`/`fix/**`. -- `dev-release.yml` — triggers on push to `main`. -- `rc-release.yml` — triggers on `vX.Y.Z-rc.N` tag push. -- `release.yml` — triggers on any other `v*` tag push. - -All four run on a self-hosted runner (`hestia`) inside a pinned Arch -container — not the host's native environment. The Containerfile/build -script are shared across bread-ecosystem products and live in -`bread-ecosystem/ci/`; this repo's `ci/build.sh` clones that repo at the -sha in `ci/bread-ecosystem.rev` (deliberately pinned, not `main`) and -delegates to it. Nothing runs automatically on plain commits or PRs -beyond what's listed. - -## Architecture - -Three crates: - -| Crate | Role | -|---|---| -| `breadclipd` | Clipboard-watch daemon; persists history to SQLite | -| `breadclip` | GTK4 Layer Shell popup (thin UI over the same DB) | -| `breadclip-core` | Shared history schema / DB access | - -`--screenshot` (`breadclip/src/screenshot.rs`) captures the history panel -through `bread-screenshots`; do not rewrite it just to retarget the crate pin. - -`EVENTS.md` is the bread-event contract. App id `clip`. Implemented: -`bread.clip.copied`, `bread.clip.clear.done`/`.failed`, and command -`bread.command.clip.clear`. There is no pin/select — the history schema has -no pinned column and the popup has no pin UI. Do not invent -`bread.command.clip.pin`/`.select` (or matching events) ahead of a real -product feature. - -## Don't -- Don't embed credentials in remote URLs — SSH or a credential helper only. -- Don't invent pin/select on the event bus. See `EVENTS.md`. From fc704f4d67b16b9e9234dae30d54bd4c6f8c4af6 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 22:32:33 +0800 Subject: [PATCH 17/23] gitignore: exclude graphify-out local cache --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 36f7f5b..272a817 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,9 @@ logs/ # Runtime files *.sock *.pid + +# Local hygiene notes (not for commit) +CLAUDE.md + +# graphify knowledge-graph output (local tool cache, not for commit) +graphify-out/ From fe8ea86c87e0c52d9c3aad2d3e4a6c1945f5cbef Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 22:53:46 +0800 Subject: [PATCH 18/23] Pin bread-ecosystem crates to v0.7.2 Repoint bread-theme, bread-utils, and bread-screenshots at tag v0.7.2 on git.breadway.dev. Drop the 69ce2d67 rev pin now that screenshots is on a tagged release. --- Cargo.lock | 30 ++++++++++-------------------- breadclip-core/Cargo.toml | 2 +- breadclip/Cargo.toml | 9 +++------ breadclipd/Cargo.toml | 2 +- 4 files changed, 15 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f678d95..aa7763c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,11 +93,11 @@ dependencies = [ [[package]] name = "bread-screenshots" -version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d#69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" +version = "0.7.2" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73" dependencies = [ "anyhow", - "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?rev=69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d)", + "bread-utils", "tracing", ] @@ -114,8 +114,8 @@ dependencies = [ [[package]] name = "bread-theme" -version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" +version = "0.7.2" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73" dependencies = [ "dirs", "gtk4", @@ -125,8 +125,8 @@ dependencies = [ [[package]] name = "bread-utils" -version = "0.3.1" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1#db2fa3c4b4c1e6933bc5cf62a236d05972fdc886" +version = "0.7.2" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73" dependencies = [ "bread-shared", "dirs", @@ -136,16 +136,6 @@ dependencies = [ "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", - "serde_json", -] - [[package]] name = "breadclip" version = "0.2.1" @@ -153,7 +143,7 @@ dependencies = [ "anyhow", "bread-screenshots", "bread-theme", - "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", + "bread-utils", "breadclip-core", "clap", "gtk4", @@ -165,7 +155,7 @@ dependencies = [ name = "breadclip-core" version = "0.2.1" dependencies = [ - "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", + "bread-utils", "dirs", "hex", "rusqlite", @@ -177,7 +167,7 @@ dependencies = [ name = "breadclipd" version = "0.2.1" dependencies = [ - "bread-utils 0.3.1 (git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.1)", + "bread-utils", "breadclip-core", "serde_json", "tempfile", diff --git a/breadclip-core/Cargo.toml b/breadclip-core/Cargo.toml index c57a76c..904ba09 100644 --- a/breadclip-core/Cargo.toml +++ b/breadclip-core/Cargo.toml @@ -8,7 +8,7 @@ rusqlite = { version = "0.31", features = ["bundled"] } sha2 = "0.10" hex = "0.4" dirs = "5" -bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1" } +bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2" } [dev-dependencies] tempfile = "3" diff --git a/breadclip/Cargo.toml b/breadclip/Cargo.toml index ef0b350..9d6edfa 100644 --- a/breadclip/Cargo.toml +++ b/breadclip/Cargo.toml @@ -9,13 +9,10 @@ path = "src/main.rs" [dependencies] breadclip-core = { path = "../breadclip-core" } -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", features = ["gtk"] } +bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["gtk"] } +bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["gtk"] } # Capture primitives for `--screenshot` mode — see src/screenshot.rs. -# Not in bread-ecosystem v0.7.1 (crate landed after that tag); same remote -# as the tag-pinned deps, floating on main until the next ecosystem release. -# v0.7.1 predates this crate; rev-pin so this is not `branch = "main"`. -bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", rev = "69ce2d67a8de8a09c064aa9d7ff99b46656f0b1d" } +bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2" } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" serde_json = "1" diff --git a/breadclipd/Cargo.toml b/breadclipd/Cargo.toml index 8e4a9b4..bb50065 100644 --- a/breadclipd/Cargo.toml +++ b/breadclipd/Cargo.toml @@ -9,7 +9,7 @@ path = "src/main.rs" [dependencies] breadclip-core = { path = "../breadclip-core" } -bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.1", features = ["bread-client"] } +bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["bread-client"] } serde_json = "1" [dev-dependencies] From 999b73bf3373ad65332b95e897aa7614d9d917cc Mon Sep 17 00:00:00 2001 From: Breadway Date: Sat, 15 Aug 2026 23:05:47 +0800 Subject: [PATCH 19/23] Bump version to v0.2.2 --- Cargo.lock | 6 +++--- breadclip-core/Cargo.toml | 2 +- breadclip/Cargo.toml | 2 +- breadclipd/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa7763c..848f6cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,7 +138,7 @@ dependencies = [ [[package]] name = "breadclip" -version = "0.2.1" +version = "0.2.2" dependencies = [ "anyhow", "bread-screenshots", @@ -153,7 +153,7 @@ dependencies = [ [[package]] name = "breadclip-core" -version = "0.2.1" +version = "0.2.2" dependencies = [ "bread-utils", "dirs", @@ -165,7 +165,7 @@ dependencies = [ [[package]] name = "breadclipd" -version = "0.2.1" +version = "0.2.2" dependencies = [ "bread-utils", "breadclip-core", diff --git a/breadclip-core/Cargo.toml b/breadclip-core/Cargo.toml index 904ba09..086c7de 100644 --- a/breadclip-core/Cargo.toml +++ b/breadclip-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadclip-core" -version = "0.2.1" +version = "0.2.2" edition = "2021" [dependencies] diff --git a/breadclip/Cargo.toml b/breadclip/Cargo.toml index 9d6edfa..c23cd98 100644 --- a/breadclip/Cargo.toml +++ b/breadclip/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadclip" -version = "0.2.1" +version = "0.2.2" edition = "2021" [[bin]] diff --git a/breadclipd/Cargo.toml b/breadclipd/Cargo.toml index bb50065..a9d584f 100644 --- a/breadclipd/Cargo.toml +++ b/breadclipd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadclipd" -version = "0.2.1" +version = "0.2.2" edition = "2021" [[bin]] From 74553fa19c16839aca3e42c71e76c8330a917338 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sun, 16 Aug 2026 00:26:07 +0800 Subject: [PATCH 20/23] Adopt bread_utils::screenshot_cli for --screenshot flags Replace the local settle delay, canvas defaults, and pair-validation error path with bread-utils v0.7.2. Clap parsing stays in-tree. --- breadclip/src/screenshot.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/breadclip/src/screenshot.rs b/breadclip/src/screenshot.rs index b4fd8fa..0337bd7 100644 --- a/breadclip/src/screenshot.rs +++ b/breadclip/src/screenshot.rs @@ -13,15 +13,10 @@ //! `bread-capture`'s isolation module), so the panel always falls back to //! centered, which is exactly what we want for a consistent screenshot. +use bread_utils::screenshot_cli::{validate_pair, DEFAULT_HEIGHT, DEFAULT_WIDTH, SETTLE_DELAY}; use clap::Parser; use gtk4::prelude::*; use std::path::PathBuf; -use std::time::Duration; - -/// Extra settle time after `map` for the first frame to actually paint -/// before grim runs — `map` fires once the surface exists, not once -/// anything has been drawn into it. -const SETTLE_DELAY: Duration = Duration::from_millis(300); #[derive(Parser)] #[command(name = "breadclip")] @@ -37,11 +32,11 @@ pub struct Cli { /// Capture canvas width — matches the isolated compositor's output width /// (`bread-capture --isolate-width`). - #[arg(long, default_value_t = 1920)] + #[arg(long, default_value_t = DEFAULT_WIDTH)] pub width: u32, /// Capture canvas height — see `width`. - #[arg(long, default_value_t = 1080)] + #[arg(long, default_value_t = DEFAULT_HEIGHT)] pub height: u32, } @@ -54,16 +49,20 @@ pub struct ScreenshotRequest { } impl Cli { - /// `None` for a normal run. Exits the process with an error if - /// `--screenshot` was given without `--output`, before any GTK setup + /// `None` for a normal run. Exits the process with an error if the + /// `--screenshot` / `--output` pair is incomplete, before any GTK setup /// happens. pub fn screenshot_request(&self) -> Option { - let view = self.screenshot.clone()?; - let Some(output) = self.output.clone() else { - eprintln!("breadclip: --screenshot requires --output"); + if let Err(e) = validate_pair(self.screenshot.as_deref(), self.output.as_deref()) { + eprintln!("breadclip: {e}"); std::process::exit(1); - }; - Some(ScreenshotRequest { view, output, width: self.width, height: self.height }) + } + Some(ScreenshotRequest { + view: self.screenshot.clone()?, + output: self.output.clone()?, + width: self.width, + height: self.height, + }) } } From c959153d5a8a13038445eb9fd82e4b4f6cd998b3 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sun, 16 Aug 2026 00:50:22 +0800 Subject: [PATCH 21/23] CI: refuse unsigned bakery index on stable tag releases --- .forgejo/workflows/release.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index cd68a3a..0105b92 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -17,7 +17,16 @@ jobs: "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src - name: build - run: cd src && bash ci/build.sh cargo build --release --locked + run: | + set -euo pipefail + if [ ! -f src/ci/build.sh ]; then + echo "::error::ci/build.sh is missing — bakery release builds must go through the shared CI wrapper" + exit 1 + fi + cd src && bash ci/build.sh cargo build --release --locked || { + echo "::error::cargo build --release --locked failed. If Cargo.lock drifted, update and commit it; do not drop --locked." + exit 1 + } - name: prepare artifacts run: | @@ -36,8 +45,14 @@ jobs: ln -sfn "${VERSION}" "/srv/breadway-dl/breadclip/latest" - name: regenerate index.json + env: + MINISIGN_SEC_KEY: ${{ secrets.BAKERY_MINISIGN_SEC_KEY_PATH }} run: | set -euo pipefail + if [ -z "${MINISIGN_SEC_KEY:-}" ]; then + echo "::error::BAKERY_MINISIGN_SEC_KEY_PATH secret not set — refusing to regenerate index.json unsigned (would leave a stale signature mismatched against fresh content and break bakery for everyone)" + exit 1 + fi rm -rf /tmp/bread-ecosystem-ci git clone https://git.breadway.dev/Breadway/bread-ecosystem.git /tmp/bread-ecosystem-ci bash /tmp/bread-ecosystem-ci/scripts/gen-index.sh From ed41e7e1ae01e34780ccd599b1046387d81faf84 Mon Sep 17 00:00:00 2001 From: Breadway Date: Sun, 16 Aug 2026 13:24:18 +0800 Subject: [PATCH 22/23] Bind the clip popup to the current monitor's bread-theme palette Pin bread-theme to v0.7.4. --- Cargo.lock | 157 +++++++++++++++++++++++------------------- breadclip/Cargo.toml | 2 +- breadclip/src/main.rs | 1 + 3 files changed, 87 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 848f6cb..509849e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,8 +114,8 @@ dependencies = [ [[package]] name = "bread-theme" -version = "0.7.2" -source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.2#30517f161724132cdeb658c04cf5e490be07ee73" +version = "0.7.4" +source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.7.4#fcba3760387e2523edb71350f8efea3bc851b21e" dependencies = [ "dirs", "gtk4", @@ -193,14 +193,14 @@ checksum = "f8b4985713047f5faee02b8db6a6ef32bbb50269ff53c1aee716d1d195b76d54" dependencies = [ "glib-sys", "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] name = "cc" -version = "1.3.0" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" +checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" dependencies = [ "find-msvc-tools", "shlex", @@ -224,9 +224,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clap" -version = "4.6.4" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", "clap_derive", @@ -234,9 +234,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.2" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -348,9 +348,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "field-offset" @@ -364,30 +364,30 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "futures-channel" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -396,32 +396,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.3", ] [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-macro", @@ -452,7 +452,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -485,7 +485,7 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -546,7 +546,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 7.0.8", "windows-sys 0.61.2", ] @@ -610,7 +610,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "030967459f9f676851872c6304adea7825c6d462ec9b72554c733cf0c5952233" dependencies = [ "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -621,7 +621,7 @@ checksum = "22a861859b887a79cf461359c192c97a57d8fb0229dd291232e57aa11f6fa72c" dependencies = [ "glib-sys", "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -642,7 +642,7 @@ checksum = "5c7ffdfde88f3570d3705e0d8a2433e036d387a1f2930bbf47eafcb5f569fd04" dependencies = [ "glib-sys", "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -673,7 +673,7 @@ dependencies = [ "graphene-sys", "libc", "pango-sys", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -699,9 +699,9 @@ dependencies = [ [[package]] name = "gtk4-layer-shell" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4069987ff4793699511a251028cc336b438e46565b463f111250148d574752a" +checksum = "17c28ea0f4676fdaaae7ff2413a24d0d35c8657424f84856c1103c73454c9da4" dependencies = [ "bitflags", "gdk4", @@ -714,15 +714,15 @@ dependencies = [ [[package]] name = "gtk4-layer-shell-sys" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f566a5ec5bcc454e7fcf2ab76930887ced5365afce12c1e5201bb296b95f1b9" +checksum = "bcf19bb884ef0ef55b9e6b2b369c39b4fcc0c41e3a0c1cbc8c267720338b690b" dependencies = [ "gdk4-sys", "glib-sys", "gtk4-sys", "libc", - "system-deps", + "system-deps 8.0.0", ] [[package]] @@ -753,7 +753,7 @@ dependencies = [ "gsk4-sys", "libc", "pango-sys", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -822,15 +822,15 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libredox" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +checksum = "28d0a00925a9f930d679b6789b721e3a7f9ed110f41b86d2497caa780c3a070a" dependencies = [ "libc", ] @@ -911,7 +911,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps", + "system-deps 7.0.8", ] [[package]] @@ -922,9 +922,9 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "proc-macro-crate" @@ -937,18 +937,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -1014,9 +1014,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -1024,29 +1024,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -1139,7 +1139,20 @@ dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml 1.1.3+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", + "version-compare", +] + +[[package]] +name = "system-deps" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83779a5c956bcb6ba627a4ecf0a9d7625db47d7537e0892d97f712ac995648a3" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml 1.1.4+spec-1.1.0", "version-compare", ] @@ -1196,9 +1209,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.3+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap", "serde_core", @@ -1255,9 +1268,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow 1.0.4", ] @@ -1448,24 +1461,24 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.28" +version = "0.8.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" +checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" [[package]] name = "zerocopy" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", diff --git a/breadclip/Cargo.toml b/breadclip/Cargo.toml index c23cd98..2de120a 100644 --- a/breadclip/Cargo.toml +++ b/breadclip/Cargo.toml @@ -9,7 +9,7 @@ path = "src/main.rs" [dependencies] breadclip-core = { path = "../breadclip-core" } -bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["gtk"] } +bread-theme = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.4", features = ["gtk"] } bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2", features = ["gtk"] } # Capture primitives for `--screenshot` mode — see src/screenshot.rs. bread-screenshots = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.7.2" } diff --git a/breadclip/src/main.rs b/breadclip/src/main.rs index 05b0dc8..4d2858e 100644 --- a/breadclip/src/main.rs +++ b/breadclip/src/main.rs @@ -203,6 +203,7 @@ fn run_ui(entries: Vec, screenshot_req: Option Date: Sun, 16 Aug 2026 14:09:21 +0800 Subject: [PATCH 23/23] Bump version to v0.2.4 --- Cargo.lock | 6 +++--- breadclip-core/Cargo.toml | 2 +- breadclip/Cargo.toml | 2 +- breadclipd/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 509849e..07dd579 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,7 +138,7 @@ dependencies = [ [[package]] name = "breadclip" -version = "0.2.2" +version = "0.2.4" dependencies = [ "anyhow", "bread-screenshots", @@ -153,7 +153,7 @@ dependencies = [ [[package]] name = "breadclip-core" -version = "0.2.2" +version = "0.2.4" dependencies = [ "bread-utils", "dirs", @@ -165,7 +165,7 @@ dependencies = [ [[package]] name = "breadclipd" -version = "0.2.2" +version = "0.2.4" dependencies = [ "bread-utils", "breadclip-core", diff --git a/breadclip-core/Cargo.toml b/breadclip-core/Cargo.toml index 086c7de..2742fb8 100644 --- a/breadclip-core/Cargo.toml +++ b/breadclip-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadclip-core" -version = "0.2.2" +version = "0.2.4" edition = "2021" [dependencies] diff --git a/breadclip/Cargo.toml b/breadclip/Cargo.toml index 2de120a..3274612 100644 --- a/breadclip/Cargo.toml +++ b/breadclip/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadclip" -version = "0.2.2" +version = "0.2.4" edition = "2021" [[bin]] diff --git a/breadclipd/Cargo.toml b/breadclipd/Cargo.toml index a9d584f..da1eeca 100644 --- a/breadclipd/Cargo.toml +++ b/breadclipd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadclipd" -version = "0.2.2" +version = "0.2.4" edition = "2021" [[bin]]