Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ than a number. Both are recorded in every run's `manifest.json`.

## [Unreleased]

### Changed

- The desktop application builds against `sha2` 0.11. Its `finalize()` returns
`hybrid_array::Array` rather than the old `GenericArray`, which does not implement
`LowerHex`, so the four `format!("{:x}", ..)` sites move to a `components::hex`
helper. The strings are unchanged, and a test pins them against the canonical
SHA-256 vectors, because they are compared with published checksums and used as
cache directory names.

## [0.2.0] - 2026-09-08

Three code reviews and their fixes (`docs/29_code_review_2026-09-07.md`,
Expand Down
78 changes: 71 additions & 7 deletions desktop/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ serde_json = "1"
ureq = { version = "3", default-features = false, features = ["rustls"] }
# The downloaded bytes are executed, so they are checksummed against a pinned
# digest first.
sha2 = "0.10"
sha2 = "0.11"
# Unpacking the ThermoRawFileParser release, which is a zip. Pure Rust, and
# `deflate` only: shelling out would mean `unzip` on Linux and `tar`/PowerShell on
# Windows, neither of which is guaranteed present.
Expand Down
38 changes: 38 additions & 0 deletions desktop/src-tauri/src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,22 @@ impl Env {
/// Packages whose version is worth reporting, because it changes results.
const REPORT_VERSIONS: &[&str] = &["deeplc", "torch", "mokapot", "ms2pip", "numpy"];

/// Lowercase hex of a digest.
///
/// `sha2` 0.11 returns `hybrid_array::Array` from `finalize()` rather than the old
/// `GenericArray`, and that type does not implement `LowerHex`, so the `format!("{:x}",
/// ..)` this replaced stopped compiling. Writing the bytes out keeps the string
/// identical, which matters: these digests are compared against published checksums and
/// used as cache directory names, so a changed spelling would reject good downloads and
/// miss every existing cache entry.
pub fn hex(bytes: impl AsRef<[u8]>) -> String {
use std::fmt::Write as _;
bytes.as_ref().iter().fold(String::new(), |mut s, b| {
let _ = write!(s, "{b:02x}");
s
})
}

/// Per-user application data, where the managed environment is created.
///
/// Not beside the executable: on Windows that is under Program Files, which a
Expand Down Expand Up @@ -572,6 +588,28 @@ pub fn install(installer: Arc<Installer>, env: Env) -> Result<(), String> {
mod tests {
use super::*;

#[test]
fn hex_matches_the_lowerhex_spelling_it_replaced() {
// These strings are compared against published SHA-256 checksums and used as
// cache directory names, so the spelling is a compatibility surface, not a
// detail: a changed one would reject good downloads and miss every existing
// cache entry. `sha2` 0.11 stopped implementing `LowerHex` on its digest type,
// which is why the formatting moved here.
use sha2::{Digest, Sha256};
assert_eq!(
hex(Sha256::digest(b"")),
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"the empty-input SHA-256, as every other tool prints it"
);
assert_eq!(
hex(Sha256::digest(b"abc")),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
// Zero-padded, lowercase, no separators, and an empty input is an empty string.
assert_eq!(hex([0x00u8, 0x0f, 0xff]), "000fff");
assert_eq!(hex([]), "");
}

#[test]
fn both_requirement_sets_are_compiled_in_and_look_right() {
// Requirement lines only. The comments legitimately discuss MS2PIP at
Expand Down
6 changes: 3 additions & 3 deletions desktop/src-tauri/src/diann.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ fn download_verified(installer: &Arc<Installer>, a: &Asset, dest: &Path) -> Resu
std::io::Write::flush(&mut file).map_err(|e| e.to_string())?;
drop(file);

let got = format!("{:x}", hasher.finalize());
let got = crate::components::hex(hasher.finalize());
if got != a.sha256 {
// The file is removed rather than left for someone to run by hand.
let _ = std::fs::remove_file(dest);
Expand Down Expand Up @@ -943,7 +943,7 @@ pub fn library_cache_dir(req: &BuildRequest, diann_version: &str) -> Result<Path
)
.as_bytes(),
);
let key = format!("{:x}", h.finalize());
let key = crate::components::hex(h.finalize());
Ok(crate::components::data_dir()
.join("libraries")
.join(&key[..16]))
Expand Down Expand Up @@ -1757,7 +1757,7 @@ mod tests {
fn a_download_that_matches_its_digest_is_kept() {
use sha2::{Digest, Sha256};
let body = b"the quick brown fox jumps over the lazy dog".to_vec();
let digest = format!("{:x}", Sha256::digest(&body));
let digest = crate::components::hex(Sha256::digest(&body));
let url: &'static str = Box::leak(serve_once(body.clone()).into_boxed_str());
let sha: &'static str = Box::leak(digest.into_boxed_str());

Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/src/thermo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ fn download(installer: &Arc<Installer>, a: &Asset, dest: &std::path::Path) -> Re
std::io::Write::flush(&mut file).map_err(|e| e.to_string())?;
drop(file);

let got = format!("{:x}", hasher.finalize());
let got = crate::components::hex(hasher.finalize());
if got != a.sha256 {
let _ = std::fs::remove_file(dest);
return Err(format!(
Expand Down