mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 19:07:04 +01:00
i3blocks: issue_dashboard block
Some checks failed
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (mypy, mypy) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (pylint, pylint) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (rest, ) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (shellcheck, shellcheck) (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / lib-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Python tests / test (push) Has been cancelled
Some checks failed
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (mypy, mypy) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (pylint, pylint) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (rest, ) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (shellcheck, shellcheck) (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / lib-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Python tests / test (push) Has been cancelled
[issue_dashboard] shows what the local issue dashboard (~/src/issue-dashboard) knows, from the status.json its server rewrites on every refresh: "📋 <to do> · ⚙ <agents working> · ✓ <ready to merge>", green when something is ready to merge, red with "· ✗ <n>" when an agent or a proof failed, grey "📋 dashboard" when the file is missing or stale (server not running). Zero forks on the hot path; a click opens issue-dashboard-desktop, which reuses or starts the server and opens its window. Behaviour + fork-count test in tests/test_i3blocks_issue_dashboard.sh. Prunes the oldest evidence artifact to stay at the 60-file cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WXd5qap8DWqZCS9mjh3CzH
This commit is contained in:
parent
d9038042cb
commit
63465cffac
@ -0,0 +1,39 @@
|
||||
{
|
||||
"intent": "Make the issue dashboard (~/src/issue-dashboard) one click away from the i3 bar: an [issue_dashboard] block shows issues to do, agents working and work ready to merge, turns red when an agent or a proof failed, and a click opens the dashboard window the same way [book_guard] opens book-guard.",
|
||||
"scope": [
|
||||
"linux_configuration/i3blocks/issue_dashboard.sh - new block script",
|
||||
"linux_configuration/i3blocks/config - [issue_dashboard] entry after [book_guard], interval=30",
|
||||
"linux_configuration/tests/test_i3blocks_issue_dashboard.sh - behaviour test",
|
||||
"Non-goal: the dashboard itself (lives in ~/src/issue-dashboard, local repo); i3/i3blocks were not reloaded on the live session."
|
||||
],
|
||||
"changes": [
|
||||
"issue_dashboard.sh reads the one-line status.json the dashboard server rewrites on every refresh (~/data/issue-dashboard/status.json): '📋 <to do> · ⚙ <working> · ✓ <ready>', white normally, green when something is ready to merge, red with '· ✗ <n>' when an agent or a proof failed, grey '📋 dashboard' when the file is missing, unreadable or older than 180 s (server not running). Zero forks on the hot path: one builtin read, bash regex, printf clock.",
|
||||
"A click runs ~/.local/bin/issue-dashboard-desktop via setsid -f, which reuses the dashboard server or starts it detached and opens an app window.",
|
||||
"~/.config/i3blocks/issue_dashboard.sh symlinked to the repo file, the way i3/install.sh links every i3blocks/* file."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "bash linux_configuration/tests/test_i3blocks_issue_dashboard.sh",
|
||||
"result": "pass",
|
||||
"evidence": "fresh / ready / failed / stale / missing / broken / empty / click asserted; strace shows exactly one execve (bash) and no clone/fork on the hot path."
|
||||
},
|
||||
{
|
||||
"command": "bash linux_configuration/tests/test_i3blocks_efficiency.sh",
|
||||
"result": "pass",
|
||||
"evidence": "All i3blocks efficiency regression tests passed."
|
||||
},
|
||||
{
|
||||
"command": "~/.config/i3blocks/issue_dashboard.sh (live status.json from the real server)",
|
||||
"result": "pass",
|
||||
"evidence": "'📋 5 · ⚙ 0 · ✓ 0' #FFFFFF while the server ran against Sepci0/kado-no-akari; '📋 dashboard' #6272A4 before it had ever run; BLOCK_BUTTON=1 started the launcher, which opened the (stubbed) browser with --app=http://127.0.0.1:8765/."
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"The block appears only after the next i3 reload; nothing was reloaded on the live session.",
|
||||
"A click with issue-dashboard-desktop missing does nothing (errors are silenced so the bar never breaks)."
|
||||
],
|
||||
"rollback": [
|
||||
"git revert this commit and remove ~/.config/i3blocks/issue_dashboard.sh; i3-msg reload.",
|
||||
"Confirm the bar still renders [book_guard] and the other blocks."
|
||||
]
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
{
|
||||
"intent": "Stop the Python tests workflow installing ~62 linters and formatters it never invokes, by giving it a requirements file scoped to what the suite actually needs.",
|
||||
"scope": [
|
||||
"meta/requirements-test.txt (new)",
|
||||
".github/workflows/python-tests.yml",
|
||||
".pre-commit-config.yaml (new requirements-test-sync hook)",
|
||||
"Non-goal: changing pre-commit.yml, which genuinely needs every formatter"
|
||||
],
|
||||
"changes": [
|
||||
"Added meta/requirements-test.txt: 17 packages, down from 79. Derived by static import analysis over python_pkg/ and linux_configuration/tests/, then verified by running the suite against only that list.",
|
||||
"python-tests.yml installs the subset, caches on it, and lists it in both path filters so a change to it triggers the workflow.",
|
||||
"Added a requirements-test-sync pre-commit hook reusing check_requirements_sync.py's existing --requirements flag, so a new third-party import cannot silently break CI with a ModuleNotFoundError.",
|
||||
"The pytest-* plugins are included deliberately: randomly, timeout, sugar and xdist change how the suite runs even though nothing imports them."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "python3.11 venv + pip install -r meta/requirements-test.txt + full pytest run with coverage",
|
||||
"result": "pass",
|
||||
"evidence": "2174 passed, TOTAL 100.00%, 7138 statements / 1748 branches, 0 missing -- identical to the full-requirements run"
|
||||
},
|
||||
{
|
||||
"command": "pip install -r meta/requirements-test.txt (timed)",
|
||||
"result": "pass",
|
||||
"evidence": "6.2s for 17 packages against a warm local cache; the full 79-package list took 49-55s on the CI runner"
|
||||
},
|
||||
{
|
||||
"command": "python3 meta/scripts/check_requirements_sync.py --package python_pkg --requirements meta/requirements-test.txt",
|
||||
"result": "pass",
|
||||
"evidence": "exit 0 -- every third-party import in python_pkg is declared in the subset"
|
||||
},
|
||||
{
|
||||
"command": "pre-commit run requirements-test-sync --all-files",
|
||||
"result": "pass",
|
||||
"evidence": "Passed"
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"A new runtime import that is not added here would fail CI. Mitigated by the new hook, which gates on the same analysis and runs on every python_pkg .py change.",
|
||||
"The subset can drift from the full list's version bounds. Both are >= ranges on the same packages, so a divergence surfaces as a resolver conflict rather than a silent mismatch."
|
||||
],
|
||||
"rollback": [
|
||||
"Point the Install dependencies step and cache-dependency-path back at requirements.txt and delete the hook.",
|
||||
"After rollback, confirm the install step returns to ~50s."
|
||||
]
|
||||
}
|
||||
@ -63,6 +63,10 @@ interval=30
|
||||
command=~/.config/i3blocks/book_guard.sh
|
||||
interval=30
|
||||
|
||||
[issue_dashboard]
|
||||
command=~/.config/i3blocks/issue_dashboard.sh
|
||||
interval=30
|
||||
|
||||
[shutdown_countdown]
|
||||
command=~/.config/i3blocks/shutdown_countdown.sh
|
||||
interval=60
|
||||
|
||||
83
linux_configuration/i3blocks/issue_dashboard.sh
Executable file
83
linux_configuration/i3blocks/issue_dashboard.sh
Executable file
@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# issue-dashboard status for i3blocks: issues to do, agents working, work
|
||||
# ready to merge.
|
||||
#
|
||||
# Opened the same way as book_guard.sh: a click runs issue-dashboard-desktop,
|
||||
# which reuses the dashboard server if it is answering (starting it detached
|
||||
# if not) and opens an app window onto it.
|
||||
#
|
||||
# Reads the status.json the dashboard server rewrites on every refresh (every
|
||||
# poll period, ~45 s, with or without a browser open) -- no network, no gh.
|
||||
# Zero forks on the hot path: one builtin read, regex matches and a printf
|
||||
# clock. Never blank: a missing or stale file means the server is not
|
||||
# running, and is shown as a grey "dashboard" rather than old numbers.
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly STATUS="${ISSUE_DASHBOARD_STATUS:-$HOME/data/issue-dashboard/status.json}"
|
||||
readonly LAUNCHER="${ISSUE_DASHBOARD_DESKTOP:-$HOME/.local/bin/issue-dashboard-desktop}"
|
||||
# Four missed refreshes: past this the server is gone, not just slow.
|
||||
readonly STALE_SECONDS=180
|
||||
|
||||
# Dracula-ish palette, matching the other blocks in this bar.
|
||||
readonly RED='#FF5555'
|
||||
readonly GREEN='#50FA7B'
|
||||
readonly WHITE='#FFFFFF'
|
||||
readonly GREY='#6272A4'
|
||||
|
||||
emit() {
|
||||
# full_text, short_text, color — the three lines i3blocks reads.
|
||||
printf '%s\n%s\n%s\n' "$1" "$2" "$3"
|
||||
}
|
||||
|
||||
# The only fork, and only on a click: open (or reuse) the dashboard window.
|
||||
if [[ ${BLOCK_BUTTON:-0} -ne 0 ]]; then
|
||||
setsid -f "$LAUNCHER" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# status.json is one line of flat JSON written atomically by the server.
|
||||
line=''
|
||||
if [[ -r $STATUS ]]; then
|
||||
IFS= read -r line <"$STATUS" || true
|
||||
fi
|
||||
|
||||
# field <name>: the integer value of a top-level key, or "" if absent.
|
||||
field() {
|
||||
if [[ $line =~ \"$1\":\ *([0-9]+) ]]; then
|
||||
printf -v "$1" '%s' "${BASH_REMATCH[1]}"
|
||||
else
|
||||
printf -v "$1" '%s' ''
|
||||
fi
|
||||
}
|
||||
|
||||
field todo
|
||||
field working
|
||||
field ready
|
||||
field failed
|
||||
field updated_epoch
|
||||
|
||||
if [[ -z $todo || -z $working || -z $ready || -z $failed || -z $updated_epoch ]]; then
|
||||
emit '📋 dashboard' '📋' "$GREY"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf -v now '%(%s)T' -1
|
||||
if ((now - updated_epoch > STALE_SECONDS)); then
|
||||
emit '📋 dashboard' '📋' "$GREY"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
text="📋 ${todo} · ⚙ ${working} · ✓ ${ready}"
|
||||
short="📋 ${todo}"
|
||||
color=$WHITE
|
||||
if ((failed > 0)); then
|
||||
text="$text · ✗ ${failed}"
|
||||
color=$RED
|
||||
elif ((ready > 0)); then
|
||||
color=$GREEN
|
||||
fi
|
||||
|
||||
emit "$text" "$short" "$color"
|
||||
116
linux_configuration/tests/test_i3blocks_issue_dashboard.sh
Executable file
116
linux_configuration/tests/test_i3blocks_issue_dashboard.sh
Executable file
@ -0,0 +1,116 @@
|
||||
#!/bin/bash
|
||||
# Behaviour tests for the i3blocks issue_dashboard block: counts, colours,
|
||||
# every failure state, the click, and that the hot path forks nothing. The
|
||||
# status file is a fixture and the launcher a stub, so nothing reads the real
|
||||
# dashboard state or opens a window.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||
REPO_DIR=$(cd -- "$SCRIPT_DIR/.." && pwd)
|
||||
BLOCK="$REPO_DIR/i3blocks/issue_dashboard.sh"
|
||||
TMP_DIR=$(mktemp -d)
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_eq() {
|
||||
local expected=$1
|
||||
local actual=$2
|
||||
local context=$3
|
||||
if [[ "$expected" != "$actual" ]]; then
|
||||
fail "$context (expected '$expected', actual '$actual')"
|
||||
fi
|
||||
}
|
||||
|
||||
BIN_DIR="$TMP_DIR/bin"
|
||||
mkdir -p "$BIN_DIR"
|
||||
cat >"$BIN_DIR/launcher" <<STUB
|
||||
#!/bin/bash
|
||||
echo clicked >>"$TMP_DIR/clicks"
|
||||
STUB
|
||||
chmod +x "$BIN_DIR/launcher"
|
||||
|
||||
STATUS="$TMP_DIR/status.json"
|
||||
|
||||
# status <todo> <working> <ready> <failed> <age-seconds>
|
||||
status() {
|
||||
local epoch
|
||||
printf -v epoch '%(%s)T' -1
|
||||
epoch=$((epoch - $5))
|
||||
printf '{"repo": "Sepci0/kado-no-akari", "todo": %s, "working": %s, "ready": %s, "failed": %s, "updated": "x", "updated_epoch": %s}\n' \
|
||||
"$1" "$2" "$3" "$4" "$epoch" >"$STATUS"
|
||||
}
|
||||
|
||||
run_block() {
|
||||
ISSUE_DASHBOARD_STATUS="$STATUS" ISSUE_DASHBOARD_DESKTOP="$BIN_DIR/launcher" \
|
||||
bash "$BLOCK"
|
||||
}
|
||||
|
||||
line() {
|
||||
sed -n "${1}p" <<<"$2"
|
||||
}
|
||||
|
||||
printf 'Checking issue_dashboard block states...\n'
|
||||
|
||||
status 4 1 0 0 5
|
||||
out=$(run_block)
|
||||
assert_eq '📋 4 · ⚙ 1 · ✓ 0' "$(line 1 "$out")" 'fresh text'
|
||||
assert_eq '📋 4' "$(line 2 "$out")" 'short text'
|
||||
assert_eq '#FFFFFF' "$(line 3 "$out")" 'nothing to merge is white'
|
||||
|
||||
status 4 1 2 0 5
|
||||
out=$(run_block)
|
||||
assert_eq '📋 4 · ⚙ 1 · ✓ 2' "$(line 1 "$out")" 'ready text'
|
||||
assert_eq '#50FA7B' "$(line 3 "$out")" 'something to merge is green'
|
||||
|
||||
status 3 0 2 1 5
|
||||
out=$(run_block)
|
||||
assert_eq '📋 3 · ⚙ 0 · ✓ 2 · ✗ 1' "$(line 1 "$out")" 'failed text'
|
||||
assert_eq '#FF5555' "$(line 3 "$out")" 'a failed agent is red'
|
||||
|
||||
printf 'Checking failure states are never blank or stale numbers...\n'
|
||||
|
||||
status 4 1 2 0 600
|
||||
out=$(run_block)
|
||||
assert_eq '📋 dashboard' "$(line 1 "$out")" 'stale status'
|
||||
assert_eq '#6272A4' "$(line 3 "$out")" 'stale is grey'
|
||||
|
||||
rm "$STATUS"
|
||||
out=$(run_block)
|
||||
assert_eq '📋 dashboard' "$(line 1 "$out")" 'missing status'
|
||||
assert_eq '#6272A4' "$(line 3 "$out")" 'missing is grey'
|
||||
|
||||
printf '{' >"$STATUS"
|
||||
assert_eq '📋 dashboard' "$(line 1 "$(run_block)")" 'broken status'
|
||||
: >"$STATUS"
|
||||
assert_eq '📋 dashboard' "$(line 1 "$(run_block)")" 'empty status'
|
||||
|
||||
printf 'Checking the hot path forks nothing...\n'
|
||||
|
||||
status 4 1 2 0 5
|
||||
if command -v strace >/dev/null 2>&1; then
|
||||
ISSUE_DASHBOARD_STATUS="$STATUS" strace -f -o "$TMP_DIR/trace" -e trace=execve,clone,clone3,fork,vfork \
|
||||
bash "$BLOCK" >/dev/null
|
||||
assert_eq '1' "$(grep -c 'execve(' "$TMP_DIR/trace")" 'only bash itself is exec-ed'
|
||||
assert_eq '0' "$(grep -cE 'clone3?\(|fork\(' "$TMP_DIR/trace" || true)" 'no child processes'
|
||||
else
|
||||
printf 'strace not installed; skipping the fork count\n'
|
||||
fi
|
||||
|
||||
printf 'Checking a click opens the dashboard window...\n'
|
||||
|
||||
BLOCK_BUTTON=1 run_block >/dev/null
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
[[ -s "$TMP_DIR/clicks" ]] && break
|
||||
sleep 0.1
|
||||
done
|
||||
assert_eq 'clicked' "$(cat "$TMP_DIR/clicks" 2>/dev/null)" 'click runs the launcher'
|
||||
|
||||
printf 'issue_dashboard block: all checks passed\n'
|
||||
Loading…
Reference in New Issue
Block a user