From 3b28fc65f5de7c6ad69bee48b2d1f52dd9b30972 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 29 Jun 2026 02:16:43 +0900 Subject: fix(advisory): pass IO as shared handle to Auditor::audit Auditor::audit took io as &mut dyn IOInterface, forcing the audit and installer post-audit call sites to hold a borrow_mut() on the shared IO RefCell for the whole call. During advisory fetching the repositories write to their own clones of the same handle, so the borrow_mut() collided with their borrow() and panicked with 'RefCell already mutably borrowed' on 'audit --locked'. Take the Rc> handle instead so writes borrow briefly and never overlap. Un-ignore the locked-audit regression test that this unblocks. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/shirabe/src/advisory/auditor.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'crates/shirabe/src/advisory/auditor.rs') diff --git a/crates/shirabe/src/advisory/auditor.rs b/crates/shirabe/src/advisory/auditor.rs index 662a940..402483c 100644 --- a/crates/shirabe/src/advisory/auditor.rs +++ b/crates/shirabe/src/advisory/auditor.rs @@ -4,6 +4,7 @@ use crate::advisory::AnySecurityAdvisory; use crate::advisory::SecurityAdvisory; use crate::io::ConsoleIO; use crate::io::IOInterface; +use crate::io::IOInterfaceImmutable; use crate::json::JsonFile; use crate::package::CompletePackageInterfaceHandle; use crate::package::PackageInterfaceHandle; @@ -17,6 +18,8 @@ use shirabe_php_shim::{ DATE_ATOM, InvalidArgumentException, PhpMixed, array_all, array_any, array_key_exists, array_keys, array_reduce, get_class, sprintf, str_starts_with, }; +use std::cell::RefCell; +use std::rc::Rc; /// Shape of the `--format=json` audit output. #[derive(serde::Serialize)] @@ -112,7 +115,7 @@ impl Auditor { #[allow(clippy::too_many_arguments, reason = "to keep PHP signature")] pub fn audit( &self, - io: &mut dyn IOInterface, + io: &Rc>, repo_set: &RepositorySet, packages: Vec, format: &str, @@ -425,13 +428,14 @@ impl Auditor { /// @param self::FORMAT_* $format The format that will be used to output audit results. fn output_advisories( &self, - io: &mut dyn IOInterface, + io: &Rc>, advisories: &IndexMap>, format: &str, ) -> anyhow::Result<()> { match format { Self::FORMAT_TABLE => { - let io_as_console = io.as_any().downcast_ref::(); + let io_ref = io.borrow(); + let io_as_console = io_ref.as_any().downcast_ref::(); if io_as_console.is_none() { return Err(InvalidArgumentException { message: format!( @@ -524,7 +528,7 @@ impl Auditor { /// @param array> $advisories fn output_advisories_plain( &self, - io: &mut dyn IOInterface, + io: &Rc>, advisories: &IndexMap>, ) -> anyhow::Result<()> { let mut error: Vec = vec![]; @@ -571,7 +575,7 @@ impl Auditor { /// @param self::FORMAT_PLAIN|self::FORMAT_TABLE $format fn output_abandoned_packages( &self, - io: &mut dyn IOInterface, + io: &Rc>, packages: &[CompletePackageInterfaceHandle], format: &str, ) -> anyhow::Result<()> { @@ -602,7 +606,8 @@ impl Auditor { return Ok(()); } - let io_as_console = io.as_any().downcast_ref::(); + let io_ref = io.borrow(); + let io_as_console = io_ref.as_any().downcast_ref::(); if io_as_console.is_none() { return Err(InvalidArgumentException { message: format!( -- cgit v1.3.1