From 76b0a8f7f518700463490f0f0ddfbee084d1c8f9 Mon Sep 17 00:00:00 2001 From: MotherSphere Date: Thu, 8 Oct 2026 13:48:07 +0200 Subject: [PATCH 1/6] docs: replace the unfilled SECURITY.md template with Grape's policy --- SECURITY.md | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 034e848..c26ee03 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,21 +1,16 @@ # Security Policy -## Supported Versions +## Supported versions -Use this section to tell people about which versions of your project are -currently being supported with security updates. +Only the **latest release** receives security fixes. Grape ships as a single +binary, through Colony and the release page, so the fix for a vulnerability is +the next release, not a patch to an older one. -| Version | Supported | -| ------- | ------------------ | -| 5.1.x | :white_check_mark: | -| 5.0.x | :x: | -| 4.0.x | :white_check_mark: | -| < 4.0 | :x: | +## Reporting a vulnerability -## Reporting a Vulnerability +Please report vulnerabilities **privately** via +[GitHub Security Advisories](https://github.com/Project-Colony/Grape/security/advisories/new) +("Report a vulnerability"). Do not open a public issue for exploitable bugs. -Use this section to tell people how to report a vulnerability. - -Tell them where to go, how often they can expect to get an update on a -reported vulnerability, what to expect if the vulnerability is accepted or -declined, etc. +Include the version (`grape --version`), the platform, and the steps or file +that reproduce it. The report stays private until a fixed release is out. From 58aa3f285e6cc7445804e919b93b4c5859c71c0c Mon Sep 17 00:00:00 2001 From: MotherSphere Date: Thu, 8 Oct 2026 13:49:55 +0200 Subject: [PATCH 2/6] docs: describe the CI that now builds and tests every pull request --- docs/internals/contributing.md | 22 +++++++++++++--------- docs/internals/packaging.md | 5 +++-- docs/project/roadmap.md | 16 +++++++++++----- scripts/README.md | 2 +- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/docs/internals/contributing.md b/docs/internals/contributing.md index 16dcebe..65b7e30 100644 --- a/docs/internals/contributing.md +++ b/docs/internals/contributing.md @@ -34,7 +34,8 @@ allows. A new warning anywhere fails the build, which is deliberate. ## Checks before committing -There is no CI that runs them, so the hooks are how they get run. +CI builds and tests every pull request (see below), but it does not run +rustfmt or clippy yet, so the hooks are how those two get run. ```bash ./scripts/setup-hooks.sh # rustfmt + clippy + cargo test on every commit @@ -64,14 +65,17 @@ Say this plainly, because it is the part that surprises people. tests need a real output device and are marked `#[ignore]`, so a default `cargo test` runs four of them. Playback, seeking, gapless and the EQ are verified by hand. -- **Nothing runs the tests automatically.** `.github/workflows/` contains - `release.yml` and nothing else — no test, clippy or fmt job. CI compiles four - targets when a release is cut, and never runs a test. The git hooks in - `scripts/` are the entire safety net, and they are opt-in. -- **Windows and macOS are compile-verified only.** The release matrix builds - both, so those paths typecheck every release, but the LaunchAgent, the HKCU - autostart, the `tray-icon` backend and the global hotkeys have no automated - exercise on either OS. Linux is the platform actually run. +- **CI does not gate rustfmt or clippy.** `.github/workflows/ci.yml` runs on + every pull request and every push to `main`: `cargo build --all-targets` and + `cargo test` on Linux, Windows and macOS, a build at the declared minimum + Rust (1.90), and a check of `colony.json` against the published schema. The + header of `ci.yml` says why rustfmt and clippy are not gated yet; until they + are, the opt-in git hooks in `scripts/` are the only thing running them. +- **Windows and macOS are built and tested, not run.** CI compiles and runs the + suite on both, and a release starts the Windows and Apple Silicon binaries + with `--version`, but the LaunchAgent, the HKCU autostart, the `tray-icon` + backend and the global hotkeys have no automated exercise on either OS. Linux + is the platform actually run. - **Last.fm is never contacted by a test.** The online tests cover parsing, caching, the TTL and the backoff against fixtures. The live API is not in the loop, and the code path needs a user-supplied key to do anything at all. diff --git a/docs/internals/packaging.md b/docs/internals/packaging.md index e847a22..537c4dc 100644 --- a/docs/internals/packaging.md +++ b/docs/internals/packaging.md @@ -45,8 +45,9 @@ job sees a key. The Windows job also checks that `grape-windows.exe` carries the version resource `build.rs` writes: ProductName `Grape` and the release version as ProductVersion, which SignPath requires before it signs. -That build is also the only automated check Windows and macOS ever get: those -targets typecheck at release time and are never tested. See +A release build is not the first time Windows and macOS compile: `ci.yml` +builds and tests on both for every pull request. Neither workflow exercises the +tray, autostart or hotkeys there; see [contributing.md](contributing.md#what-the-tests-do-not-cover). ## 3. Everything is signed, then published diff --git a/docs/project/roadmap.md b/docs/project/roadmap.md index ac756ea..a146804 100644 --- a/docs/project/roadmap.md +++ b/docs/project/roadmap.md @@ -62,8 +62,13 @@ here as complete but are not have been moved down to *Started and unfinished*. itself when the platform says no. - Linux tray on `ksni` / StatusNotifierItem, after `tray-icon`'s GTK 3 backend turned out to abort the process. -- Signed releases: four targets, ed25519 signatures verified at sign time, and - a release that fails rather than shipping unsigned. +- Signed releases: four targets built and checked, then the shared + Project-Colony sign-and-publish workflow writes ed25519 `.sig`, `.meta` and + `.meta.sig`, verifies them on the release, and only then publishes it. A + failed run leaves a draft rather than shipping unsigned. Windows Authenticode + through SignPath is wired and waits for SignPath to accept the project. +- CI on every pull request: build and `cargo test` on Linux, Windows and + macOS, a build at the minimum supported Rust, and a `colony.json` check. ## Started and unfinished @@ -95,9 +100,10 @@ Either finish it or remove the control; leaving it is the worst of the three. `preferences.json`. - **Expose sorting.** `SortOption` implements alphabetical, by album, by year and by duration, and no control emits it — the order is permanently by album. -- **Run the tests in CI.** `.github/workflows/` has only `release.yml`. The - hooks under `scripts/` are opt-in and the only thing running clippy, rustfmt - or `cargo test` today. +- **Gate rustfmt and clippy in CI.** `ci.yml` runs neither: `rustfmt.toml` + asks for nightly-only options and edition 2024 on an edition 2021 crate, and + clippy has a pedantic/nursery backlog. The opt-in hooks under `scripts/` are + the only thing running them today. Fix both, then add the gate. - **Cover the audio path.** Nineteen of the twenty-three player tests are `#[ignore]` for want of an output device. - **Report real scan progress.** The scan already runs on the tokio executor diff --git a/scripts/README.md b/scripts/README.md index e8e2624..453d7e8 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -60,7 +60,7 @@ cargo fmt --all -- --check # Run clippy cargo clippy --all-targets --all-features -# Strict mode (as in CI) +# Strict mode (as the git hooks run it; CI does not run clippy yet) cargo clippy --all-targets --all-features -- -D warnings ``` From 4759d2d991b0da81a42af48d71b6ebcb4cf6575d Mon Sep 17 00:00:00 2001 From: MotherSphere Date: Thu, 8 Oct 2026 13:49:55 +0200 Subject: [PATCH 3/6] docs: present sign-release.sh as the manual fallback, not the release path --- docs/internals/contributing.md | 2 +- docs/internals/packaging.md | 6 ++++++ scripts/sign-release.sh | 14 ++++++++------ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/docs/internals/contributing.md b/docs/internals/contributing.md index 65b7e30..e0eaecf 100644 --- a/docs/internals/contributing.md +++ b/docs/internals/contributing.md @@ -121,7 +121,7 @@ Enough of them exist that the shape is settled: ``` assets/ logos, application icons, the bundled JetBrains Mono Nerd Font docs/ these pages -scripts/ the git hooks +scripts/ the git hooks, and sign-release.sh (the manual signing fallback) src/ the program — see architecture.md tests/ integration tests ``` diff --git a/docs/internals/packaging.md b/docs/internals/packaging.md index 537c4dc..304996c 100644 --- a/docs/internals/packaging.md +++ b/docs/internals/packaging.md @@ -71,6 +71,12 @@ If a run fails after the tag exists, finish the draft from the tag: gh workflow run release.yml -R Project-Colony/Grape --ref vX.Y.Z -f tag=vX.Y.Z ``` +`scripts/sign-release.sh` writes the same three files by hand. No workflow +uses it: it is the manual fallback for when the shared workflow cannot run, and +it needs the release private key, which is not in the repository. It only +signs and checks its own signatures; it does no Authenticode, and uploading the +files and publishing the draft are left to whoever runs it. + ## 4. Colony picks it up `colony.json` is the launcher manifest: diff --git a/scripts/sign-release.sh b/scripts/sign-release.sh index 43d496d..6ff3bcb 100755 --- a/scripts/sign-release.sh +++ b/scripts/sign-release.sh @@ -4,8 +4,9 @@ # ".sig" that the launcher verifies before applying a self-update. # # The signature is the raw 64-byte ed25519 signature over the asset bytes, as -# produced by `openssl pkeyutl -sign -rawin` — the same format src/signing.rs -# verifies against the embedded public key. openssl is the only dependency. +# produced by `openssl pkeyutl -sign -rawin` - the same format Colony's +# src/signing.rs verifies against its embedded public key. openssl is the only +# dependency. # # A signature over raw bytes proves only "these bytes came from the org" — not # WHICH artefact or WHICH version they are. So each asset also gets a signed @@ -28,10 +29,11 @@ # COLONY_SIGNING_KEY=/path/to/colony-release.pem \ # COLONY_RELEASE_VERSION=v1.2.3 ./scripts/sign-release.sh [ ...] # -# In CI, provide the private key via a secret (e.g. write it to a temp file from -# a GitHub Actions secret) and set COLONY_SIGNING_KEY to its path. Upload every -# generated ".sig", ".meta" and ".meta.sig" as release -# assets alongside their binary. +# Releases do not use this script. CI signs in the shared sign-and-publish +# workflow of Project-Colony-Resources, which writes the same three files (see +# docs/internals/packaging.md). This is the manual fallback for when that +# workflow cannot run: upload every generated ".sig", ".meta" and +# ".meta.sig" to the draft release alongside their binary, by hand. set -euo pipefail KEY="${COLONY_SIGNING_KEY:-$HOME/.config/colony/release-signing/colony-release.pem}" From d7f1a52125723271ac9f0c7859cee450f0c3c9e2 Mon Sep 17 00:00:00 2001 From: MotherSphere Date: Thu, 8 Oct 2026 13:49:55 +0200 Subject: [PATCH 4/6] docs: list the .meta sidecars that ship with every release asset --- README.md | 7 ++++--- docs/guide/install.md | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cb498b4..4bd344e 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ and because releases are signed, Colony verifies the signature before installing Grab the asset for your platform from the [latest release](https://github.com/Project-Colony/Grape/releases/latest). Each -one ships with a matching `.sig` file. +one ships with a matching `.sig`, `.meta` and `.meta.sig`. | Platform | Asset | |---|---| @@ -165,8 +165,9 @@ There is no installer: download the asset and run it. #### Running the binaries -The `.sig` files are Colony's own ed25519 signatures. The binaries are not -signed by Apple or Microsoft, so macOS and Windows warn on first launch. +The `.sig` and `.meta.sig` files are Colony's own ed25519 signatures. The +binaries are not signed by Apple or Microsoft, so macOS and Windows warn on +first launch. - **Linux:** `chmod +x grape-linux && ./grape-linux` - **macOS:** make it executable and clear the download quarantine flag, then diff --git a/docs/guide/install.md b/docs/guide/install.md index eeb6d31..59ad08e 100644 --- a/docs/guide/install.md +++ b/docs/guide/install.md @@ -15,7 +15,8 @@ refuses an asset that does not match. ## Direct binary download -Every release ships exactly four assets, each with a detached `.sig` beside it: +Every release ships exactly four binaries, each with a detached `.sig` beside +it and a signed `.meta` (plus its `.meta.sig`) binding it to the version: | Platform | Asset | Target triple | |---|---|---| From 86bbfa89ee2be7c3939dfba9deadf56bcc543e5f Mon Sep 17 00:00:00 2001 From: MotherSphere Date: Thu, 8 Oct 2026 13:50:00 +0200 Subject: [PATCH 5/6] docs: drop a stale note about a French startup message --- docs/internals/contributing.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/internals/contributing.md b/docs/internals/contributing.md index e0eaecf..6de22e0 100644 --- a/docs/internals/contributing.md +++ b/docs/internals/contributing.md @@ -92,8 +92,6 @@ cargo test --test player_tests -- --ignored commit messages, and these documents. French is a shipped *UI locale*, which is a different thing: it lives in `src/ui/i18n.rs` as `STRINGS_FR` and stays there. - through the binary: the startup failure message in `src/main.rs` is still - French. - **Commits are Conventional Commits.** release-please parses them to decide the next version and to write `CHANGELOG.md`; a `fix:` is a patch, a `feat:` a minor. `CHANGELOG.md` is never edited by hand. From 5c31514935a309a1e5b0c3490beb559374594078 Mon Sep 17 00:00:00 2001 From: MotherSphere Date: Thu, 8 Oct 2026 13:57:10 +0200 Subject: [PATCH 6/6] docs: bring the test counts up to date --- .github/workflows/ci.yml | 2 +- docs/internals/architecture.md | 4 ++-- docs/internals/contributing.md | 10 +++++----- docs/project/roadmap.md | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 550469e..62f4d4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,7 +86,7 @@ jobs: - name: Test run: cargo test - # 19 of the player tests need a real audio device and are #[ignore]d, so + # 20 of the player tests need a real audio device and are #[ignore]d, so # this proves the suite compiles and the rest passes -- not that audio # works on a runner with no sound card. diff --git a/docs/internals/architecture.md b/docs/internals/architecture.md index 4eb099a..23bc5be 100644 --- a/docs/internals/architecture.md +++ b/docs/internals/architecture.md @@ -259,7 +259,7 @@ kept alive at all. ## Tests `tests/cache_tests.rs` (20), `tests/metadata_online_tests.rs` (20) and -`tests/player_tests.rs` (23, of which 19 are `#[ignore]` because they need a -real audio device), plus 50 `#[test]` functions inside `src/`. What that +`tests/player_tests.rs` (25, of which 20 are `#[ignore]` because they need a +real audio device), plus 65 `#[test]` functions inside `src/`. What that coverage does and does not prove is in [contributing.md](contributing.md#what-the-tests-do-not-cover). diff --git a/docs/internals/contributing.md b/docs/internals/contributing.md index 6de22e0..e1d1ea0 100644 --- a/docs/internals/contributing.md +++ b/docs/internals/contributing.md @@ -52,18 +52,18 @@ Formatting is `rustfmt.toml`; lints are the `.cargo/config.toml` block above. | | | |---|---| -| `src/**` | 50 `#[test]` functions — settings normalization and clamping, the theme migration, album-artist inference, cache-path validation, EQ clamping, the migration marker | +| `src/**` | 65 `#[test]` functions, 2 of them `#[ignore]` (one needs an audio device, one a private D-Bus session): settings normalization and clamping, the theme migration, album-artist inference, cache-path validation, EQ clamping, the migration marker, the native media-session state | | `tests/cache_tests.rs` | 20 tests over the `.grape_cache/` round trip and signature invalidation | | `tests/metadata_online_tests.rs` | 20 tests over Last.fm response parsing, the TTL, and the backoff | -| `tests/player_tests.rs` | 23 tests, **19 of them `#[ignore]`** | +| `tests/player_tests.rs` | 25 tests, **20 of them `#[ignore]`** | ### What the tests do not cover Say this plainly, because it is the part that surprises people. -- **The audio path is barely tested.** Nineteen of the twenty-three player - tests need a real output device and are marked `#[ignore]`, so a default - `cargo test` runs four of them. Playback, seeking, gapless and the EQ are +- **The audio path is barely tested.** Twenty of the twenty-five player tests + need a real output device and are marked `#[ignore]`, so a default + `cargo test` runs five of them. Playback, seeking, gapless and the EQ are verified by hand. - **CI does not gate rustfmt or clippy.** `.github/workflows/ci.yml` runs on every pull request and every push to `main`: `cargo build --all-targets` and diff --git a/docs/project/roadmap.md b/docs/project/roadmap.md index a146804..11c1cfd 100644 --- a/docs/project/roadmap.md +++ b/docs/project/roadmap.md @@ -104,7 +104,7 @@ Either finish it or remove the control; leaving it is the worst of the three. asks for nightly-only options and edition 2024 on an edition 2021 crate, and clippy has a pedantic/nursery backlog. The opt-in hooks under `scripts/` are the only thing running them today. Fix both, then add the gate. -- **Cover the audio path.** Nineteen of the twenty-three player tests are +- **Cover the audio path.** Twenty of the twenty-five player tests are `#[ignore]` for want of an output device. - **Report real scan progress.** The scan already runs on the tokio executor (`ui/app/playback.rs`), but the banner's bar is a 120 ms cosmetic loop that