Merge remote-tracking branch 'origin/main' into feature/extract-heph-nvim
Some checks failed
Build / validate (pull_request) Failing after 14m23s

# Conflicts:
#	.dagger/src/hephaestus_ci/main.py
This commit is contained in:
Erich Blume 2026-06-03 23:24:10 -07:00
commit 9fa9a9b9c1
7 changed files with 2380 additions and 452 deletions

View file

@ -1,30 +0,0 @@
#!/usr/bin/env bash
#
# Project build hook (tech-spec §9): run the Rust workspace checks in CI.
#
# Invoked by .forgejo/workflows/build.yaml after the generic `prek` pass.
# Bootstraps a minimal Rust toolchain via rustup when the runner image lacks
# one, so the suite runs without a bespoke CI image. The headless nvim e2e
# suite is added here once heph.nvim exists.
set -euo pipefail
if ! command -v cargo >/dev/null 2>&1; then
echo "cargo not found; bootstrapping Rust toolchain via rustup..."
export RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}"
export CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}"
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --profile minimal
# shellcheck disable=SC1091
. "$CARGO_HOME/env"
rustup component add clippy rustfmt
fi
echo "== cargo fmt --check =="
cargo fmt --all --check
echo "== cargo clippy =="
cargo clippy --all-targets --all-features -- -D warnings
echo "== cargo test =="
cargo test --all

2719
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -14,16 +14,16 @@ version = "0.0.0"
license = "LicenseRef-Proprietary"
publish = false
authors = ["Erich Blume <blume.erich@gmail.com>"]
rust-version = "1.85"
rust-version = "1.89"
[workspace.dependencies]
rusqlite = { version = "0.32", features = ["bundled"] }
rusqlite = { version = "0.40", features = ["bundled"] }
ulid = "1"
thiserror = "2"
anyhow = "1"
pulldown-cmark = { version = "0.13", default-features = false }
rrule = "0.13"
yrs = "0.26"
rrule = "0.14"
yrs = "0.27"
chrono = { version = "0.4", default-features = false, features = ["clock"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
@ -38,16 +38,11 @@ tokio = { version = "1", features = [
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
clap = { version = "4", features = ["derive"] }
ratatui = "0.29"
fs4 = "0.12"
ratatui = "0.30"
axum = "0.8"
jsonwebtoken = { version = "10", features = ["rust_crypto"] }
keyring = { version = "3", features = [
"apple-native",
"sync-secret-service",
"crypto-rust",
"vendored",
] }
keyring = { version = "4" }
keyring-core = "1"
ureq = { version = "3", features = ["json"] }
reqwest = { version = "0.13", default-features = false, features = [
"json",

View file

@ -27,10 +27,10 @@ thiserror.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
clap.workspace = true
fs4.workspace = true
axum.workspace = true
jsonwebtoken.workspace = true
keyring.workspace = true
keyring-core.workspace = true
reqwest.workspace = true
ureq.workspace = true

View file

@ -14,7 +14,6 @@ use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use fs4::fs_std::FileExt;
/// Holds the exclusive lock for as long as it lives; drops release it.
pub struct LockGuard {
@ -35,9 +34,10 @@ impl LockGuard {
.open(&path)
.with_context(|| format!("opening lock file {}", path.display()))?;
// A failure here (typically `WouldBlock`) means another process holds
// the lock.
file.try_lock_exclusive().map_err(|e| {
// `File::try_lock` (std, stable since Rust 1.89) takes the advisory
// lock without blocking. A failure here (typically `WouldBlock`) means
// another process holds it.
file.try_lock().map_err(|e| {
anyhow::anyhow!(
"store {} is already locked by another hephd process: {e}",
db_path.display()

View file

@ -88,8 +88,19 @@ impl KeyringTokenStore {
}
}
fn entry(&self) -> Result<keyring::Entry, AuthError> {
keyring::Entry::new(&self.service, &self.account)
fn entry(&self) -> Result<keyring_core::Entry, AuthError> {
// keyring 4 splits the cross-platform `Entry`/`Error` types into
// `keyring_core` and requires a credential store to be registered
// before any entry is built. Register the OS-native store once,
// lazily, on first use (idempotent across both surfaces).
static NATIVE_STORE: std::sync::Once = std::sync::Once::new();
NATIVE_STORE.call_once(|| {
// `not_keyutils = true`: on Linux prefer the Secret Service over
// the kernel keyutils store, which is wiped on logout/reboot and
// would silently drop a persisted login token.
let _ = keyring::use_native_store(true);
});
keyring_core::Entry::new(&self.service, &self.account)
.map_err(|e| AuthError::Provider(e.to_string()))
}
}
@ -108,7 +119,7 @@ impl TokenStore for KeyringTokenStore {
fn clear(&self) -> Result<(), AuthError> {
match self.entry()?.delete_credential() {
Ok(()) => Ok(()),
Err(keyring::Error::NoEntry) => Ok(()),
Err(keyring_core::Error::NoEntry) => Ok(()),
Err(e) => Err(AuthError::Provider(e.to_string())),
}
}

View file

@ -0,0 +1 @@
Pre-v1 dependency-refresh sweep: bumped all external crates to latest stable — `keyring` 3→4 (the new `keyring_core` split + register-the-native-store model), `rusqlite` 0.32→0.40, `ratatui` 0.29→0.30, `rrule` 0.13→0.14, `yrs` 0.26→0.27, plus semver-compatible updates across the rest. Dropped the `fs4` dependency in favor of `std::fs::File::try_lock` (stable since Rust 1.89), raising the workspace MSRV to 1.89. Removed the orphaned `.forgejo/scripts/build` hook (CI calls Dagger directly). Full suite green: 228 Rust tests + 25 heph.nvim headless e2e specs, `clippy -D warnings` + `fmt` clean.