Merge pull request #57 from nixops4/flake-update

`flake.lock`: Update
This commit is contained in:
hercules-ci[bot] 2026-03-02 22:58:51 +00:00 committed by GitHub
commit 7de15fa260
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 167 additions and 42 deletions

View file

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- `primop::RecoverableError` for primop errors that should not be memoized in the thunk, allowing retry on next force. Required by Nix >= 2.34 ([release note](https://nix.dev/manual/nix/2.34/release-notes/rl-2.34.html#c-api-changes)) for recoverable errors to remain recoverable, as Nix 2.34 memoizes errors by default.
## [0.2.0] - 2026-01-13
### Added

30
dev/flake.lock generated
View file

@ -24,11 +24,11 @@
]
},
"locked": {
"lastModified": 1768135262,
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
"lastModified": 1769996383,
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
"type": "github"
},
"original": {
@ -63,11 +63,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1768476106,
"narHash": "sha256-V0YOJRum50gtKgwavsAfwXc9+XAsJCC7386YZx1sWGQ=",
"lastModified": 1771131391,
"narHash": "sha256-HPBNYf7HiKtBVy7/69vKpLYHX6wTcUxndxmybzDlXP8=",
"owner": "hercules-ci",
"repo": "hercules-ci-effects",
"rev": "c19e263e6e22ec7379d972f19e6a322f943c73fb",
"rev": "0b152e0f7c5cc265a529cd63374b80e2771b207b",
"type": "github"
},
"original": {
@ -78,11 +78,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1768305791,
"narHash": "sha256-AIdl6WAn9aymeaH/NvBj0H9qM+XuAuYbGMZaP0zcXAQ=",
"lastModified": 1771008912,
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1412caf7bf9e660f2f962917c14b1ea1c3bc695e",
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
"type": "github"
},
"original": {
@ -99,11 +99,11 @@
"nixpkgs": []
},
"locked": {
"lastModified": 1769069492,
"narHash": "sha256-Efs3VUPelRduf3PpfPP2ovEB4CXT7vHf8W+xc49RL/U=",
"lastModified": 1772024342,
"narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "a1ef738813b15cf8ec759bdff5761b027e3e1d23",
"rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476",
"type": "github"
},
"original": {
@ -124,11 +124,11 @@
"nixpkgs": []
},
"locked": {
"lastModified": 1769691507,
"narHash": "sha256-8aAYwyVzSSwIhP2glDhw/G0i5+wOrren3v6WmxkVonM=",
"lastModified": 1770228511,
"narHash": "sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD+Fyxk=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b",
"rev": "337a4fe074be1042a35086f15481d763b8ddc0e7",
"type": "github"
},
"original": {

48
flake.lock generated
View file

@ -77,11 +77,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1768135262,
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
"lastModified": 1769996383,
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
"type": "github"
},
"original": {
@ -170,11 +170,11 @@
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1769798267,
"narHash": "sha256-vpI7XEfX5zeCVRANUzhMNsZfrMWuN0rwNenQ3z0rJNo=",
"lastModified": 1772224943,
"narHash": "sha256-jJIlRLPPVYu860MVFx4gsRx3sskmLDSRWXXue5tYncw=",
"owner": "NixOS",
"repo": "nix",
"rev": "77b6b01b727f0cd1324e431a32a8854768b957ef",
"rev": "0acd0566e85e4597269482824711bcde7b518600",
"type": "github"
},
"original": {
@ -196,11 +196,11 @@
"treefmt": "treefmt"
},
"locked": {
"lastModified": 1769840916,
"narHash": "sha256-bjtDp0NHjLjDOjklQVHCDCVM5q39zDzuwenNri0p4Ys=",
"lastModified": 1772260057,
"narHash": "sha256-NaUqM0i6XIGdgRNxxQ9sfgCAVeE2Ko9rz7e19RsNUKw=",
"owner": "90-008",
"repo": "nix-cargo-integration",
"rev": "6d583e2098fa3df490c2597df06386e3efcc39b6",
"rev": "c783c5dff02c06f2af6226d4dd4d494542d0a4d2",
"type": "github"
},
"original": {
@ -211,11 +211,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1769461804,
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
"lastModified": 1772198003,
"narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
"rev": "dd9b079222d43e1943b6ebd802f04fd959dc8e61",
"type": "github"
},
"original": {
@ -243,11 +243,11 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1765674936,
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"lastModified": 1769909678,
"narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"rev": "72716169fe93074c333e8d0173151350670b824c",
"type": "github"
},
"original": {
@ -280,11 +280,11 @@
]
},
"locked": {
"lastModified": 1768135262,
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
"lastModified": 1769996383,
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
"type": "github"
},
"original": {
@ -355,11 +355,11 @@
]
},
"locked": {
"lastModified": 1769828398,
"narHash": "sha256-zmnvRUm15QrlKH0V1BZoiT3U+Q+tr+P5Osi8qgtL9fY=",
"lastModified": 1772247314,
"narHash": "sha256-x6IFQ9bL7YYfW2m2z8D3Em2YtAA3HE8kiCFwai2fwrw=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "a1d32c90c8a4ea43e9586b7e5894c179d5747425",
"rev": "a1ab5e89ab12e1a37c0b264af6386a7472d68a15",
"type": "github"
},
"original": {
@ -399,11 +399,11 @@
]
},
"locked": {
"lastModified": 1769691507,
"narHash": "sha256-8aAYwyVzSSwIhP2glDhw/G0i5+wOrren3v6WmxkVonM=",
"lastModified": 1770228511,
"narHash": "sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD+Fyxk=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b",
"rev": "337a4fe074be1042a35086f15481d763b8ddc0e7",
"type": "github"
},
"original": {

View file

@ -2,5 +2,5 @@ use nix_bindings_util::nix_version::emit_version_cfg;
fn main() {
let nix_version = pkg_config::probe_library("nix-expr-c").unwrap().version;
emit_version_cfg(&nix_version, &["2.26"]);
emit_version_cfg(&nix_version, &["2.26", "2.34.0pre"]);
}

View file

@ -2845,4 +2845,81 @@ mod tests {
})
.unwrap();
}
#[test]
#[cfg(nix_at_least = "2.34.0pre")]
fn eval_state_primop_recoverable_error() {
gc_registering_current_thread(|| {
let store = Store::open(None, []).unwrap();
let mut es = EvalState::new(store, []).unwrap();
let call_count = std::cell::Cell::new(0u32);
let v = es
.new_value_thunk(
"recoverable_test",
Box::new(move |es: &mut EvalState| {
let count = call_count.get();
call_count.set(count + 1);
if count == 0 {
Err(primop::RecoverableError::new("transient failure").into())
} else {
es.new_value_int(42)
}
}),
)
.unwrap();
// First force should fail with the recoverable error
let r = es.force(&v);
assert!(r.is_err());
assert!(
r.unwrap_err().to_string().contains("transient failure"),
"Error message should contain 'transient failure'"
);
// Second force should succeed because the error was recoverable
es.force(&v).unwrap();
let i = es.require_int(&v).unwrap();
assert_eq!(i, 42);
})
.unwrap();
}
#[test]
#[cfg(nix_at_least = "2.34.0pre")]
fn eval_state_primop_recoverable_error_in_chain() {
gc_registering_current_thread(|| {
let store = Store::open(None, []).unwrap();
let mut es = EvalState::new(store, []).unwrap();
let call_count = std::cell::Cell::new(0u32);
let v = es
.new_value_thunk(
"recoverable_chain_test",
Box::new(move |es: &mut EvalState| {
let count = call_count.get();
call_count.set(count + 1);
if count == 0 {
// Wrap RecoverableError in .context(), pushing it down the chain
use anyhow::Context;
Err(primop::RecoverableError::new("transient failure"))
.context("wrapper context")?
} else {
es.new_value_int(42)
}
}),
)
.unwrap();
// First force should fail
let r = es.force(&v);
assert!(r.is_err());
// Second force should succeed if RecoverableError is found in the chain
es.force(&v).unwrap();
let i = es.require_int(&v).unwrap();
assert_eq!(i, 42);
})
.unwrap();
}
}

View file

@ -8,6 +8,35 @@ use std::ffi::{c_int, c_void, CStr, CString};
use std::mem::ManuallyDrop;
use std::ptr::{null, null_mut};
/// A primop error that is not memoized in the thunk that triggered it,
/// allowing the thunk to be forced again.
///
/// Since [Nix 2.34](https://nix.dev/manual/nix/2.34/release-notes/rl-2.34.html#c-api-changes),
/// primop errors are memoized by default: once a thunk fails, forcing it
/// again returns the same error. Use `RecoverableError` for errors that
/// are transient, so the caller can retry.
///
/// On Nix < 2.34, all errors are already recoverable, so this type has
/// no additional effect.
///
/// Available since nix-bindings-expr 0.2.1.
#[derive(Debug)]
pub struct RecoverableError(String);
impl RecoverableError {
pub fn new(msg: impl Into<String>) -> Self {
RecoverableError(msg.into())
}
}
impl std::fmt::Display for RecoverableError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::error::Error for RecoverableError {}
/// Metadata for a primop, used with `PrimOp::new`.
pub struct PrimOpMeta<'a, const N: usize> {
/// Name of the primop. Note that primops do not have to be registered as
@ -36,6 +65,11 @@ impl Drop for PrimOp {
}
}
impl PrimOp {
/// Create a new primop with the given metadata and implementation.
///
/// When `f` returns an `Err`, the error is propagated to the Nix evaluator.
/// To return a [recoverable error](RecoverableError), include it in the
/// error chain (e.g. `Err(RecoverableError::new("...").into())`).
pub fn new<const N: usize>(
eval_state: &mut EvalState,
meta: PrimOpMeta<N>,
@ -108,13 +142,22 @@ unsafe extern "C" fn function_adapter(
raw::copy_value(context_out, ret, v.raw_ptr());
},
Err(e) => unsafe {
let err_code = error_code(&e);
let cstr = CString::new(e.to_string()).unwrap_or_else(|_e| {
CString::new("<rust nix-expr application error message contained null byte>")
.unwrap()
});
raw_util::set_err_msg(context_out, raw_util::err_NIX_ERR_UNKNOWN, cstr.as_ptr());
raw_util::set_err_msg(context_out, err_code, cstr.as_ptr());
},
}
}
fn error_code(e: &anyhow::Error) -> raw_util::err {
#[cfg(nix_at_least = "2.34.0pre")]
if e.downcast_ref::<RecoverableError>().is_some() {
return raw_util::err_NIX_ERR_RECOVERABLE;
}
raw_util::err_NIX_ERR_UNKNOWN
}
static FUNCTION_ADAPTER: raw::PrimOpFun = Some(function_adapter);

View file

@ -788,7 +788,8 @@ mod tests {
Err(e) => e.to_string(),
};
assert!(
err.contains("required system or feature not available"),
err.contains("required system or feature not available")
|| err.contains("platform mismatch"),
"Error should mention system not available, got: {}",
err
);