chore: harden CI and shipped script, tidy internals (audit remediation)
A four-axis audit found no HIGH/MEDIUM issues. This addresses the remaining low-risk items and protects the release path: - ci: add cargo test + clippy -D warnings gate on master/tags - ci: reject malformed version tags before the pkgver sed substitution - fix: move the shipped waybar wrapper's cache/lock to $XDG_RUNTIME_DIR (mode-700, no cross-user symlink) instead of predictable /tmp paths - refactor: find_card() returns the open ALSA mixer, dropping a second open; collapse two if-let blocks into edition-2024 let-chains - docs: record in DECISIONS.md why the audit's "dead code" stays (it mirrors the Bragi spec) and why the 5ms HID sleep is left as-is Bump 0.1.4 -> 0.1.5: the shipped waybar script changes behavior.
This commit is contained in:
1 parent
6ec56a7968
commit
717c54c9c1
8 files changed
+61
-19
No files matched your search
@@ -0,0 +1,31 @@
|
||||
# ABOUTME: Test + lint gate on push to master and on version tags.
|
||||
# ABOUTME: Stops a broken build or regressed test from reaching a tagged release.
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: moonarch
|
||||
steps:
|
||||
- name: Checkout pushed commit
|
||||
run: |
|
||||
git clone http://gitea:3000/nevaforget/corsairctl.git repo
|
||||
cd repo
|
||||
git checkout "$GITHUB_SHA"
|
||||
|
||||
- name: cargo test
|
||||
run: |
|
||||
cd repo
|
||||
cargo test --locked
|
||||
|
||||
- name: cargo clippy
|
||||
run: |
|
||||
cd repo
|
||||
cargo clippy --all-targets -- -D warnings
|
||||
@@ -17,6 +17,10 @@ jobs:
|
||||
git clone --bare http://gitea:3000/nevaforget/corsairctl.git source.git
|
||||
cd source.git
|
||||
PKGVER=$(git describe --tags --abbrev=0 | sed 's/^v//')
|
||||
if ! echo "$PKGVER" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
|
||||
echo "Invalid pkgver: $PKGVER"
|
||||
exit 1
|
||||
fi
|
||||
echo "New pkgver: $PKGVER"
|
||||
echo "$PKGVER" > /tmp/pkgver
|
||||
|
||||
|
||||
Generated
+1
-1
@@ -144,7 +144,7 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "corsairctl"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"alsa",
|
||||
"clap",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "corsairctl"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
edition = "2024"
|
||||
description = "CLI tool for Corsair Bragi-protocol devices (HS80, etc.)"
|
||||
license = "MIT"
|
||||
|
||||
@@ -23,3 +23,9 @@
|
||||
- **Why**: The open-source publish plan (rename away from "Corsair", multi-protocol backend layer, crates.io, upstream to HeadsetControl) was dropped. The tool is for personal use across Dom's own machines, distributed as an Arch package via the private moonarch repo (Gitea registry at gitea.moonarch.de), used primarily as a Waybar widget.
|
||||
- **Tradeoffs**: GPLv3 and the rename were only justified by public distribution and porting HeadsetControl (GPLv3) code. With no public release and no GPL-derived code in the tree, copyleft and the trademark rename buy nothing. MIT is simpler and reversible — switch to GPLv3 only if GPL code is later pulled in.
|
||||
- **How**: Revert license to MIT (Cargo.toml + MIT LICENSE file). Keep the name `corsairctl`. PLAN.md retained as a record of the considered direction, no longer driving work.
|
||||
|
||||
## 2026-06-23 – Audit remediation: CI gate, script hardening; keep the protocol-spec model
|
||||
- **Who**: Dom, F.R.I.D.A.Y.
|
||||
- **Why**: A four-axis audit (quality/performance/security/hygiene) found no HIGH/MEDIUM issues; only a few low-risk items remained. Acted on the real ones, recorded what was deliberately left alone so a future audit does not re-flag it.
|
||||
- **Tradeoffs**: The audit marked unused `Property` variants, the response endpoint constant, and `STATUS_OK`/`BragiResponse.status` as "dead code". Kept on purpose — the `Property` enum mirrors the full Bragi spec (`docs/bragi-protocol.md`) and `status` captures a real device byte; deleting would lose protocol fidelity for no functional gain. The 5 ms HID pre-read sleep was also left as-is — it is a deliberate fix against observed response mix-ups (`f3a4bf8`), not arbitrary latency.
|
||||
- **How**: Added `ci.yaml` (`cargo test` + `clippy -D warnings` on master pushes and version tags) and a tag-format guard in `update-pkgver.yaml`. Moved the shipped Waybar wrapper's cache/lock from `/tmp` to `$XDG_RUNTIME_DIR` (mode-700, no cross-user symlink). `find_card()` now returns the already-open ALSA mixer instead of a card name, dropping a redundant second open. Collapsed two `if let` blocks into edition-2024 let-chains.
|
||||
@@ -2,8 +2,9 @@
|
||||
# ABOUTME: Waybar-Wrapper für corsairctl — cached JSON-Output.
|
||||
# ABOUTME: Verhindert dass mehrere Waybar-Bars gleichzeitig das HID-Gerät ansprechen.
|
||||
|
||||
CACHE="/tmp/corsairctl-waybar.json"
|
||||
LOCK="/tmp/corsairctl-waybar.lock"
|
||||
RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
||||
CACHE="$RUNTIME_DIR/corsairctl-waybar.json"
|
||||
LOCK="$RUNTIME_DIR/corsairctl-waybar.lock"
|
||||
MAX_AGE=300
|
||||
FALLBACK='{"text":"","tooltip":"","class":"offline","percentage":0}'
|
||||
|
||||
|
||||
@@ -138,15 +138,15 @@ pub fn parse_response_validated(
|
||||
}
|
||||
|
||||
// Response-Korrelation: Endpoint und Command gegen die Anfrage prüfen
|
||||
if let Some(exp) = expected_endpoint {
|
||||
if endpoint != exp {
|
||||
return Err(CorsairError::UnexpectedResponse { endpoint, command });
|
||||
}
|
||||
if let Some(exp) = expected_endpoint
|
||||
&& endpoint != exp
|
||||
{
|
||||
return Err(CorsairError::UnexpectedResponse { endpoint, command });
|
||||
}
|
||||
if let Some(exp) = expected_command {
|
||||
if command != exp {
|
||||
return Err(CorsairError::UnexpectedResponse { endpoint, command });
|
||||
}
|
||||
if let Some(exp) = expected_command
|
||||
&& command != exp
|
||||
{
|
||||
return Err(CorsairError::UnexpectedResponse { endpoint, command });
|
||||
}
|
||||
|
||||
let data = if raw.len() > 4 {
|
||||
|
||||
+7
-7
@@ -7,17 +7,19 @@ use crate::error::Result;
|
||||
|
||||
const SIDETONE_CONTROL: &str = "Sidetone";
|
||||
|
||||
/// Findet die ALSA-Card mit einem Sidetone-Control.
|
||||
/// Findet die ALSA-Card mit einem Sidetone-Control und gibt den offenen Mixer zurück.
|
||||
///
|
||||
/// Sucht nach der Karte, die ein "Sidetone" Control hat,
|
||||
/// statt sich auf den Kartennamen zu verlassen (der kann abgeschnitten sein).
|
||||
fn find_card() -> Result<String> {
|
||||
/// Gibt den bereits geöffneten Mixer zurück, damit der Aufrufer ihn nicht
|
||||
/// ein zweites Mal öffnen muss.
|
||||
fn find_card() -> Result<Mixer> {
|
||||
for card in alsa::card::Iter::new().flatten() {
|
||||
let name = format!("hw:{}", card.get_index());
|
||||
if let Ok(mixer) = Mixer::new(&name, false) {
|
||||
let selem_id = SelemId::new(SIDETONE_CONTROL, 0);
|
||||
if mixer.find_selem(&selem_id).is_some() {
|
||||
return Ok(name);
|
||||
return Ok(mixer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,8 +28,7 @@ fn find_card() -> Result<String> {
|
||||
|
||||
/// Liest den aktuellen Sidetone-Level (0-23).
|
||||
pub fn get_level() -> Result<i64> {
|
||||
let card = find_card()?;
|
||||
let mixer = Mixer::new(&card, false)?;
|
||||
let mixer = find_card()?;
|
||||
|
||||
let selem_id = SelemId::new(SIDETONE_CONTROL, 0);
|
||||
let selem = mixer
|
||||
@@ -45,8 +46,7 @@ pub fn get_level() -> Result<i64> {
|
||||
|
||||
/// Setzt den Sidetone-Level (0-23).
|
||||
pub fn set_level(level: i64) -> Result<()> {
|
||||
let card = find_card()?;
|
||||
let mixer = Mixer::new(&card, false)?;
|
||||
let mixer = find_card()?;
|
||||
|
||||
// Volume setzen
|
||||
let volume_id = SelemId::new(SIDETONE_CONTROL, 0);
|
||||
|
||||
Reference in new issue
Block a user