fix(ci): Formatierung, Root-fester I/O-Test und robuste Schlüsselsuche

- updater.rs: rustfmt-konforme Formatierung des öffentlichen Schlüssels
  (cargo fmt --check ließ die Release-Pipeline scheitern)
- storage.rs: test_io_error_* simuliert den I/O-Fehler über ein Verzeichnis
  statt chmod 000, damit er auch als root (CI-Runner) greift
- build-release.sh: findet den öffentlichen Schlüssel auch, wenn rustfmt ihn
  in die Folgezeile umbricht

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 20:30:52 +02:00
co-authored by Claude Opus 5.5
parent 9684238a53
commit 992c3daad7
3 changed files with 17 additions and 18 deletions
+2 -1
View File
@@ -33,7 +33,8 @@ sha256sum "${DEB_NAME}" "${APPIMAGE_NAME}" > "${CHECKSUM_FILE}"
# 4. Prüfsummen mit Minisign signieren (Pflicht für In-App-Updates) # 4. Prüfsummen mit Minisign signieren (Pflicht für In-App-Updates)
SIGNATURE_FILE="${CHECKSUM_FILE}.minisig" SIGNATURE_FILE="${CHECKSUM_FILE}.minisig"
rm -f "${SIGNATURE_FILE}" rm -f "${SIGNATURE_FILE}"
EMBEDDED_PUBKEY=$(grep -m1 'pub const UPDATE_SIGNING_PUBLIC_KEY' "${ROOT_DIR}/src/updater.rs" | cut -d '"' -f2) # Schlüssel kann von rustfmt in die Folgezeile umgebrochen sein
EMBEDDED_PUBKEY=$(grep -m1 -A1 'pub const UPDATE_SIGNING_PUBLIC_KEY' "${ROOT_DIR}/src/updater.rs" | grep -o '"[^"]*"' | head -n1 | tr -d '"')
if [ -n "${MINISIGN_SECRET_KEY:-}" ]; then if [ -n "${MINISIGN_SECRET_KEY:-}" ]; then
if [ -z "${EMBEDDED_PUBKEY}" ]; then if [ -z "${EMBEDDED_PUBKEY}" ]; then
echo "FEHLER: UPDATE_SIGNING_PUBLIC_KEY in src/updater.rs ist leer – diese Version könnte keine Updates prüfen." echo "FEHLER: UPDATE_SIGNING_PUBLIC_KEY in src/updater.rs ist leer – diese Version könnte keine Updates prüfen."
+13 -16
View File
@@ -1522,12 +1522,6 @@ mod tests {
#[cfg(unix)] #[cfg(unix)]
fn test_io_error_does_not_quarantine_and_does_not_overwrite() { fn test_io_error_does_not_quarantine_and_does_not_overwrite() {
use crate::document::PersistState; use crate::document::PersistState;
use std::os::unix::fs::PermissionsExt;
// Skip test if running as root
if std::env::var("USER").unwrap_or_default() == "root" {
return;
}
let temp_dir = tempfile::tempdir().expect("tempdir"); let temp_dir = tempfile::tempdir().expect("tempdir");
let path = temp_dir.path(); let path = temp_dir.path();
@@ -1535,19 +1529,17 @@ mod tests {
let manifest = NotebookManifest::new("nb_io", "Test IO"); let manifest = NotebookManifest::new("nb_io", "Test IO");
save_manifest(path, &manifest).unwrap(); save_manifest(path, &manifest).unwrap();
// I/O-Fehler simulieren, der auch als root auftritt (CI-Runner laufen oft als root,
// dort greift ein chmod 000 nicht): page_1.json ist ein Verzeichnis -> EISDIR beim Lesen
let original_content = br#"{"id": 1, "background": "Grid5mm", "strokes": []}"#; let original_content = br#"{"id": 1, "background": "Grid5mm", "strokes": []}"#;
let page_file = path.join("page_1.json"); let page_path = path.join("page_1.json");
fs::write(&page_file, original_content).unwrap(); fs::create_dir(&page_path).unwrap();
let marker = page_path.join("original");
// Make file unreadable fs::write(&marker, original_content).unwrap();
fs::set_permissions(&page_file, fs::Permissions::from_mode(0o000)).unwrap();
let (nb, mut m) = let (nb, mut m) =
load_notebook(path).expect("load_notebook should succeed with fallback page"); load_notebook(path).expect("load_notebook should succeed with fallback page");
// Restore permissions for inspection
fs::set_permissions(&page_file, fs::Permissions::from_mode(0o600)).unwrap();
let dir_entries: Vec<String> = fs::read_dir(path) let dir_entries: Vec<String> = fs::read_dir(path)
.unwrap() .unwrap()
.flatten() .flatten()
@@ -1566,10 +1558,15 @@ mod tests {
); );
save_notebook(path, &nb, &mut m).unwrap(); save_notebook(path, &nb, &mut m).unwrap();
assert!(
page_path.is_dir(),
"Original page path must not be replaced"
);
assert_eq!( assert_eq!(
fs::read(&page_file).unwrap(), fs::read(&marker).unwrap(),
original_content, original_content,
"Original file content must not be overwritten" "Original content must not be overwritten"
); );
} }
+2 -1
View File
@@ -317,7 +317,8 @@ pub fn verify_checksum(
/// signiert sein muss. Ist er leer, lehnt diese Version alle In-App-Updates ab. /// signiert sein muss. Ist er leer, lehnt diese Version alle In-App-Updates ab.
/// Erzeugen: `minisign -G -W -p ink-notes.pub -s ink-notes.key`, dann die zweite Zeile /// Erzeugen: `minisign -G -W -p ink-notes.pub -s ink-notes.key`, dann die zweite Zeile
/// aus `ink-notes.pub` hier eintragen (siehe README, Abschnitt Release-Signatur). /// aus `ink-notes.pub` hier eintragen (siehe README, Abschnitt Release-Signatur).
pub const UPDATE_SIGNING_PUBLIC_KEY: &str = "RWTZqQ0ZDCn+PHTpqgI8NwjtgtkJk5QyJ0FYHWZlCZtABsXBcdPDv9WI"; pub const UPDATE_SIGNING_PUBLIC_KEY: &str =
"RWTZqQ0ZDCn+PHTpqgI8NwjtgtkJk5QyJ0FYHWZlCZtABsXBcdPDv9WI";
/// Name des Prüfsummen-Assets eines Releases. /// Name des Prüfsummen-Assets eines Releases.
pub const CHECKSUMS_ASSET_NAME: &str = "SHA256SUMS.txt"; pub const CHECKSUMS_ASSET_NAME: &str = "SHA256SUMS.txt";