From 3d701361daec85085cc67a535839390f124b1072 Mon Sep 17 00:00:00 2001 From: randogoth Date: Thu, 10 Sep 2026 17:10:43 +0300 Subject: [PATCH] Bundle pimsync 0.5.11 in the flake package; enforce it as the version floor The Nix-packaged calcalist now wraps its PATH with a pinned pimsync 0.5.11 build, so discover works regardless of what else is on the user's PATH. discover's output format is undocumented and changed between 0.5.9 and 0.5.11 in a way the parser doesn't handle; doctor and the NotFound error now enforce 0.5.11 as a floor instead of accepting any 0.5.x patch. README's install instructions and pimsync note are corrected to match. Co-Authored-By: Claude Sonnet 5 --- README.md | 4 ++-- flake.nix | 34 ++++++++++++++++++++++++++++++++++ src/doctor.rs | 6 ++++-- src/pimsync.rs | 29 ++++++++++++++++++++++++----- 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6939fb8..a6cf791 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,10 @@ nix build # ./result/bin/calcalist Or straight from the repo, without cloning it first: ```sh -nix profile install "git+https://code.randogoth.com/randogoth/CalCalist.git" +nix profile install "git+https://code.randogoth.com/randogoth/CalCalist.git#calcalist" ``` -One runtime dependency is needed for CalDAV and iCal sync: **`pimsync` 0.5.x on `PATH`**. +One runtime dependency is needed for CalDAV and iCal sync: **`pimsync` 0.5.11 or newer on `PATH`**. The Nix flake installation method already ensures this. Then check the environment, which is worth doing before writing any config: diff --git a/flake.nix b/flake.nix index 2fd4824..0f4e8fe 100644 --- a/flake.nix +++ b/flake.nix @@ -18,6 +18,31 @@ system: let pkgs = import nixpkgs { inherit system; }; + + # nixpkgs currently ships pimsync 0.5.9, but `discover`'s output + # format is undocumented and changed between 0.5.9 and 0.5.11 — + # calcalist's parser only understands the newer shape (see + # SUPPORTED_PATCH_MIN in src/pimsync.rs). Build the exact version + # rather than trust whatever nixpkgs happens to carry. + pimsync0_5_11 = pkgs.rustPlatform.buildRustPackage (finalAttrs: { + pname = "pimsync"; + version = "0.5.11"; + src = pkgs.fetchFromSourcehut { + owner = "~whynothugo"; + repo = "pimsync"; + rev = "v${finalAttrs.version}"; + hash = "sha256-iMdBqSSguViF+54e47IGV8hH3qvTxcNkWkmND1QAAxw="; + }; + cargoHash = "sha256-dvkZ047eJnvYvyH1iW1NJo3Uv0L2T7waPYKN12bi+dA="; + + # Skips build.rs's `git describe` call, which has nothing to + # describe once fetched as a plain source tarball. + env.PIMSYNC_VERSION = finalAttrs.version; + + nativeBuildInputs = [ pkgs.pkg-config ]; + buildInputs = [ pkgs.sqlite ]; + doCheck = false; + }); in rec { calcalist = pkgs.rustPlatform.buildRustPackage { @@ -31,6 +56,15 @@ # check), not something a package build should re-run. doCheck = false; + nativeBuildInputs = [ pkgs.makeWrapper ]; + + # Ensures the pimsync calcalist finds is the one it was tested + # against, regardless of what else is on the user's PATH. + postInstall = '' + wrapProgram $out/bin/calcalist \ + --prefix PATH : ${pkgs.lib.makeBinPath [ pimsync0_5_11 ]} + ''; + meta = { description = cargoToml.package.description; license = pkgs.lib.licenses.mit; diff --git a/src/doctor.rs b/src/doctor.rs index 348e0be..4ba4902 100644 --- a/src/doctor.rs +++ b/src/doctor.rs @@ -100,9 +100,11 @@ fn check_pimsync() -> Check { let outcome = match pimsync::probe() { Ok(version) if version.is_supported() => Outcome::Ok(version.to_string()), Ok(version) => Outcome::Warn(format!( - "{version} is outside the supported {}.{}.x series; config syntax may differ", + "{version} is outside the supported {}.{}.{}+ series; \ + config syntax may differ, and older patches misreport `discover`", pimsync::SUPPORTED_MAJOR, - pimsync::SUPPORTED_MINOR + pimsync::SUPPORTED_MINOR, + pimsync::SUPPORTED_PATCH_MIN, )), Err(error) => Outcome::Fail(error.to_string()), }; diff --git a/src/pimsync.rs b/src/pimsync.rs index 1e1809b..10d8515 100644 --- a/src/pimsync.rs +++ b/src/pimsync.rs @@ -18,9 +18,12 @@ pub const BINARY: &str = "pimsync"; /// The `0.5.x` series this build generates configuration for. pimsync is /// pre-1.0 and its config syntax may change between minor releases, so the -/// range is deliberately narrow. +/// range is deliberately narrow. The patch floor matters too: `discover`'s +/// output format is undocumented and changed between 0.5.9 and 0.5.11, and +/// `parse_discovery` only understands the newer shape. pub const SUPPORTED_MAJOR: u64 = 0; pub const SUPPORTED_MINOR: u64 = 5; +pub const SUPPORTED_PATCH_MIN: u64 = 11; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Version { @@ -37,7 +40,9 @@ impl fmt::Display for Version { impl Version { pub fn is_supported(&self) -> bool { - self.major == SUPPORTED_MAJOR && self.minor == SUPPORTED_MINOR + self.major == SUPPORTED_MAJOR + && self.minor == SUPPORTED_MINOR + && self.patch >= SUPPORTED_PATCH_MIN } /// Parses the `pimsync 0.5.11` line printed by `pimsync version`. @@ -54,7 +59,9 @@ impl Version { #[derive(Debug, Error)] pub enum PimsyncError { - #[error("`{BINARY}` was not found on PATH; install it (devbox provides {BINARY}@0.5.11)")] + #[error( + "`{BINARY}` was not found on PATH; install pimsync {SUPPORTED_MAJOR}.{SUPPORTED_MINOR}.{SUPPORTED_PATCH_MIN} or newer" + )] NotFound, #[error("could not run `{BINARY}`: {0}")] Spawn(#[source] io::Error), @@ -674,17 +681,29 @@ url_command = "secret-tool lookup service calcalist account gcal-ics" let supported = Version { major: 0, minor: 5, - patch: 0, + patch: 11, }; let next_series = Version { major: 0, minor: 6, - patch: 0, + patch: 11, }; assert!(supported.is_supported()); assert!(!next_series.is_supported()); } + #[test] + fn a_patch_older_than_the_floor_is_rejected() { + // discover's output format changed between 0.5.9 and 0.5.11; older + // patches within the otherwise-supported minor series are refused. + let too_old = Version { + major: 0, + minor: 5, + patch: 9, + }; + assert!(!too_old.is_supported()); + } + /// Established against a real server: everything arrives on stdout, the /// headings are colour-coded even through a pipe, and both storages of the /// pair are reported.