From ef8d434113f7d6134709a84e97d7fd260e6c578a Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Tue, 22 Sep 2026 19:41:48 +0200 Subject: [PATCH 1/4] fix ci (linux) --- .github/workflows/nix-flake-update.yml | 65 +++++++++++++++++++++++ CLAUDE.md | 71 -------------------------- README.md | 13 ++--- flake.lock | 6 +-- 4 files changed, 72 insertions(+), 83 deletions(-) create mode 100644 .github/workflows/nix-flake-update.yml delete mode 100644 CLAUDE.md diff --git a/.github/workflows/nix-flake-update.yml b/.github/workflows/nix-flake-update.yml new file mode 100644 index 0000000..2b72815 --- /dev/null +++ b/.github/workflows/nix-flake-update.yml @@ -0,0 +1,65 @@ +# Copyright (c) 2026 l5yth +# SPDX-License-Identifier: Apache-2.0 + +# Keeps flake.lock from rotting. +# +# The `nix` job in linux.yml builds against the toolchain pinned in +# flake.lock, which lags the `dtolnay/rust-toolchain@stable` toolchain the +# Rust workflow installs. A dependency that raises its MSRV past the pinned +# rustc therefore breaks `nix` while `Rust` stays green. That is exactly how +# the Linux workflow stayed red for 16 runs in 2026 (sysinfo 0.39.x requires +# rustc 1.95; the pin shipped 1.93.0). +# +# This workflow refreshes the pin on a schedule and opens a PR. It does NOT +# run as part of the build: CI always builds the committed lock, never a +# freshly updated one, or the lock would not describe what CI tested. + +name: Nix flake update + +on: + schedule: + # Mondays 05:00 UTC, ahead of Dependabot's own weekly cargo run. + - cron: "0 5 * * 1" + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + update: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Install Nix + uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + experimental-features = nix-command flakes + + - name: Update flake.lock + run: nix flake update + + - name: Verify the updated lock builds + # A PR opened with GITHUB_TOKEN does not trigger other workflows, so + # the Linux workflow's `nix` job would not re-run on this PR. Build + # here instead: a lock that does not build never becomes a PR. + run: nix build . --print-build-logs + + - name: Open pull request + uses: peter-evans/create-pull-request@v8 + with: + branch: l5y-ci-flake-lock + delete-branch: true + commit-message: "chore: update flake.lock" + title: "chore: update flake.lock" + body: | + Automated weekly `nix flake update`. + + Verified before opening: `nix build .` succeeded against the + updated lock, which is the same check the `nix` job in the Linux + workflow runs. + labels: dependencies diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 550176d..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,71 +0,0 @@ -# Repository Guidelines - -## Purpose -`psn` is a Linux-first Rust TUI for process status navigation and signal-based process control (for example sending signals 1-9 to the selected process). - -## Project Structure & Module Organization -- `src/main.rs`: thin binary entry point only (argument parsing, wiring, startup/shutdown). -- `src/lib.rs`: crate root; re-exports public API for integration tests. -- `src/cli.rs`: command-line argument parsing (`clap`). -- `src/app.rs`: mutable application state (selection, filter, collapsed pids). -- `src/runtime.rs`: key mapping, action dispatch, event loop, terminal setup/restore. -- `src/ui.rs`: rendering only (ratatui widgets). -- `src/process.rs`: process discovery, filter compilation, sort mapping. -- `src/signal.rs`: signal mapping and send helpers. -- `src/model.rs`: shared data types (`ProcRow`). -- `src/tree.rs`: tree display order and collapse logic. -- `src/debug_tui.rs`: deterministic debug/demo mode (no real processes). -- `tests/`: integration tests. -- `.github/workflows/`: CI for formatting, linting, tests, docs, and coverage. -- `Cargo.toml`: crate metadata/dependencies. -- `packaging/`: distro packaging files (Arch/Gentoo). -- `target/`: generated build artifacts; never commit. - -Future refactor: extract `src/terminal.rs` for terminal setup/restore lifecycle (currently in `runtime.rs`). - -Module rules: -- Keep modules small and cohesive. -- Avoid cross-module cycles and hidden shared mutable state. -- Prefer pure functions for business logic; isolate side effects at boundaries. - -## Build, Test, and Development Commands -- `cargo check --all --all-features` -- `cargo run --release --` -- `cargo test --all --all-features --verbose` -- `cargo fmt --all` -- `cargo fmt --all -- --check` -- `cargo clippy --all-targets --all-features -- -D warnings` -- `RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --document-private-items` -- `cargo llvm-cov --workspace --lcov --output-path lcov.info --fail-under-lines 100` - -Before every PR, run the full quality gate locally: fmt-check, clippy, tests, docs, and coverage at 100% lines. - -## Coding Style & Naming Conventions -- Follow `rustfmt` defaults (4 spaces, no tabs). -- Naming: `snake_case` (functions/vars), `CamelCase` (types/traits), `SCREAMING_SNAKE_CASE` (constants). -- Prefer small, single-purpose functions with explicit `anyhow::Result` error propagation where appropriate. -- Keep code minimalist: no dead code, no speculative abstractions, no unused dependencies. -- Keep UI text concise, actionable, and consistent with terminal constraints. - -## Documentation Requirements -- Public and non-trivial internal APIs must have inline Rust doc comments explaining behavior and invariants. -- Document why non-obvious decisions exist (sorting rules, fallback behavior, signal mapping limits, etc.). -- Keep docs close to code and update them in the same change as logic updates. -- `cargo doc` must pass with warnings denied. - -## Testing Requirements -- Use Rust’s built-in framework (`#[test]`) for unit tests and `tests/` for integration tests. -- Every non-trivial function must have unit tests covering normal, edge, and error behavior. -- Prefer deterministic tests for sorting/filtering/signal mapping logic. -- Coverage target is strict: 100% line coverage for project code. -- If coverage is below 100%, add tests in the same PR until the gate passes. -- Test names must describe behavior (example: `refresh_rows_filters_by_name_or_cmd_case_insensitive`). - -## Commit & Pull Request Guidelines -- Use Conventional Commits (`feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`). -- Keep commits focused, buildable, and minimal. -- PRs must include a clear summary and motivation. -- PRs must include linked issue(s), when applicable. -- PRs must include test evidence (commands run and results). -- PRs must include screenshots/GIFs for user-visible TUI changes. -- PRs must explicitly call out module boundaries touched and architectural impacts. diff --git a/README.md b/README.md index 02b8400..e022e2d 100644 --- a/README.md +++ b/README.md @@ -27,16 +27,13 @@ Read more: [Dev.to: psn - a minimalist process navigator for Linux](https://dev. ## Dependencies -- GNU/Linux (process data is read from `/proc` via [sysinfo](https://crates.io/crates/sysinfo) — no external binaries required) -- Rust stable toolchain (Rust 2024 edition, Cargo) for building from source +- GNU/Linux. Process data is read from `/proc` via [sysinfo](https://crates.io/crates/sysinfo). No external binaries. +- Rust stable toolchain (Rust 2024 edition, Cargo) for building from source. Core crates: `ratatui`, `crossterm`, `sysinfo`, `nix`, `anyhow`, `users`. ## Installation -Helpers exist for Arch and Gentoo-based systems but you can install also -via crates.io or from source directly. - ### Archlinux See [PKGBUILD](./packaging/archlinux/PKGBUILD) @@ -53,8 +50,6 @@ cargo install psn ### From Source -Build from source: - ```bash git clone https://github.com/l5yth/psn.git cd psn @@ -67,7 +62,7 @@ Run the built binary: ./target/release/psn ``` -Or run directly in development: +Run without building a release binary: ```bash cargo run --release -- @@ -122,7 +117,7 @@ In-app keys: - `PgUp` / `PgDn`: page up/down - `←` / `→`: collapse / expand tree node - `/`: open interactive filter prompt (live, substring match); `Enter` confirms, `Esc` cancels -- `1`..`9`: send corresponding signal to selected process (prompts for confirmation); not available while the filter prompt is open — press `Enter` or `Esc` first +- `1`..`9`: send corresponding signal to selected process (prompts for confirmation). Unavailable while the filter prompt is open; press `Enter` or `Esc` first ## Development diff --git a/flake.lock b/flake.lock index 8d169a6..7b55dbd 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1772624091, - "narHash": "sha256-QKyJ0QGWBn6r0invrMAK8dmJoBYWoOWy7lN+UHzW1jc=", + "lastModified": 1790046670, + "narHash": "sha256-MYiI+CzL0tuWgRPjGsKCDHqYs2T3OzMlMQWOYWG0qso=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "80bdc1e5ce51f56b19791b52b2901187931f5353", + "rev": "6774f7bc253789b113a4f39285dc0fa100abeacc", "type": "github" }, "original": { From f052722cc5ded526397421f8c59ebea3d5042450 Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:01:53 +0200 Subject: [PATCH 2/4] fix ci (linux) --- .github/workflows/nix-flake-update.yml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/nix-flake-update.yml b/.github/workflows/nix-flake-update.yml index 2b72815..c58476f 100644 --- a/.github/workflows/nix-flake-update.yml +++ b/.github/workflows/nix-flake-update.yml @@ -7,8 +7,8 @@ # flake.lock, which lags the `dtolnay/rust-toolchain@stable` toolchain the # Rust workflow installs. A dependency that raises its MSRV past the pinned # rustc therefore breaks `nix` while `Rust` stays green. That is exactly how -# the Linux workflow stayed red for 16 runs in 2026 (sysinfo 0.39.x requires -# rustc 1.95; the pin shipped 1.93.0). +# the Linux workflow stayed red for 9 consecutive runs on main in 2026 +# (sysinfo 0.39.x requires rustc 1.95; the pin shipped 1.93.0). # # This workflow refreshes the pin on a schedule and opens a PR. It does NOT # run as part of the build: CI always builds the committed lock, never a @@ -18,8 +18,10 @@ name: Nix flake update on: schedule: - # Mondays 05:00 UTC, ahead of Dependabot's own weekly cargo run. - - cron: "0 5 * * 1" + # Thursdays 05:00 UTC. Dependabot's `interval: weekly` default is Monday + # 05:00 UTC, so this deliberately does not share a slot with it: the pin + # moves midweek, after that week's cargo bumps have already landed. + - cron: "0 5 * * 4" workflow_dispatch: permissions: @@ -60,6 +62,12 @@ jobs: Automated weekly `nix flake update`. Verified before opening: `nix build .` succeeded against the - updated lock, which is the same check the `nix` job in the Linux - workflow runs. + updated lock. + + Note that a PR opened with `GITHUB_TOKEN` does not trigger other + workflows, so neither `Linux` nor `Rust` runs on this PR + automatically. The build above is the substitute for the `nix` + job only; it does not cover clippy, rustdoc or coverage. Close + and reopen the PR, or push an empty commit, to get the full + gates before merging. labels: dependencies From 125b7adcf410a8d06d6509d2db2dce815996ccc6 Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:58:15 +0200 Subject: [PATCH 3/4] fix ci (linux) --- .github/workflows/linux.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 61fb398..dac870d 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -39,18 +39,28 @@ jobs: uses: actions/checkout@v7 - name: Bootstrap Gentoo repo and profile + # Sync from a daily snapshot tarball, not the live rsync mirror. + # rsync.gentoo.org updates continuously, so a sync that straddles an + # update fetches a metadata manifest that no longer matches the files + # it describes, and portage aborts: + # Manifest mismatch for metadata/md5-cache/Manifest.gz + # __size__: expected: 29048, have: 29060 + # That killed this job twice on 2026-09-22 (different file each time) + # after ~47 minutes against a ~2 minute norm. A webrsync snapshot is + # internally consistent by construction and md5/GPG verified, so the + # race cannot occur. Cost: the tree can be up to 24h old, which is + # irrelevant for validating an ebuild. run: | mkdir -p /etc/portage/repos.conf cat > /etc/portage/repos.conf/gentoo.conf <<'EOF' [gentoo] location = /var/db/repos/gentoo - sync-type = rsync - sync-uri = rsync://rsync.gentoo.org/gentoo-portage + sync-type = webrsync auto-sync = yes EOF mkdir -p /var/db/repos/gentoo - emerge --sync --quiet + emerge-webrsync --quiet if [ ! -L /etc/portage/make.profile ]; then profile_path="$(find /var/db/repos/gentoo/profiles/default/linux/amd64 -type f -name make.defaults | head -n1)" From 7ca00c3b3fc4444e9cc00c9d4543e5f7f2f64b9b Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Tue, 22 Sep 2026 22:03:16 +0200 Subject: [PATCH 4/4] bump to 0.1.5 --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 2 +- packaging/nix/default.nix | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 625b22f..351b619 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1011,7 +1011,7 @@ dependencies = [ [[package]] name = "psn" -version = "0.1.4" +version = "0.1.5" dependencies = [ "anyhow", "crossterm", diff --git a/Cargo.toml b/Cargo.toml index 26b0b33..32baaa0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "psn" -version = "0.1.4" +version = "0.1.5" edition = "2024" license = "Apache-2.0" description = "Terminal UI for process status navigation and control" diff --git a/README.md b/README.md index e022e2d..3b86561 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ cargo run --release -- ## Usage ```text -psn v0.1.4 +psn v0.1.5 process status navigator apache v2 (c) 2026 l5yth diff --git a/packaging/nix/default.nix b/packaging/nix/default.nix index 6bb74b5..5091564 100644 --- a/packaging/nix/default.nix +++ b/packaging/nix/default.nix @@ -4,7 +4,7 @@ rustPlatform.buildRustPackage { pname = "psn"; - version = "0.1.4"; + version = "0.1.5"; src = lib.cleanSource ../..;