Phase 6: IDE-Exportanbindung abschließen und Change archivieren

This commit is contained in:
2026-09-07 17:00:29 +02:00
parent 25176947ba
commit 69f2002481
31 changed files with 1310 additions and 136 deletions

View File

@@ -345,6 +345,46 @@ pub fn publish(
cancelled: &dyn Fn() -> bool,
prepare: impl FnOnce(&Path) -> Result<()>,
) -> Result<()> {
prepare_publication(path, overwrite, protected, cancelled, prepare)?.publish(cancelled)
}
/// Fertiges Staging-Ergebnis. Drop verwirft es; nur der Auftragseigner veröffentlicht.
pub struct PreparedPublication {
path: PathBuf,
protected: Vec<PathBuf>,
initial: Option<Vec<u8>>,
temporary: Temporary,
}
impl PreparedPublication {
pub fn publish(self, cancelled: &dyn Fn() -> bool) -> Result<()> {
let Self {
path,
protected,
initial,
temporary,
} = self;
ensure!(!cancelled(), "Export abgebrochen");
ensure!(
destination(&path, &protected)? == initial,
"Ziel wurde während des Exports verändert"
);
if initial.is_some() {
fs::rename(&temporary.0, &path)?;
} else {
fs::hard_link(&temporary.0, &path)
.context("Ziel inzwischen belegt oder Veröffentlichung nicht möglich")?;
}
Ok(())
}
}
pub fn prepare_publication(
path: &Path,
overwrite: bool,
protected: &[PathBuf],
cancelled: &dyn Fn() -> bool,
prepare: impl FnOnce(&Path) -> Result<()>,
) -> Result<PreparedPublication> {
ensure!(!cancelled(), "Export abgebrochen");
let initial = destination(path, protected)?;
ensure!(
@@ -371,29 +411,38 @@ pub fn publish(
prepare(&temporary.0)?;
fs::File::open(&temporary.0)?.sync_all()?;
ensure!(!cancelled(), "Export abgebrochen");
ensure!(
destination(path, protected)? == initial,
"Ziel wurde während des Exports verändert"
);
if initial.is_some() {
fs::rename(&temporary.0, path)?;
} else {
fs::hard_link(&temporary.0, path)
.context("Ziel inzwischen belegt oder Veröffentlichung nicht möglich")?;
}
Ok(())
Ok(PreparedPublication {
path: path.into(),
protected: protected.into(),
initial,
temporary,
})
}
fn codesign(path: &Path, args: &[&str]) -> Result<()> {
fn codesign(path: &Path, args: &[&str], cancelled: &dyn Fn() -> bool) -> Result<()> {
ensure!(
cfg!(target_os = "macos"),
"macOS-Finalisierung benötigt macOS mit /usr/bin/codesign"
);
let result = std::process::Command::new("/usr/bin/codesign")
let mut child = std::process::Command::new("/usr/bin/codesign")
.args(args)
.arg(path)
.output()
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::piped())
.spawn()
.context("Finalisierung: /usr/bin/codesign fehlt oder ist nicht ausführbar")?;
loop {
if cancelled() {
let _ = child.kill();
let _ = child.wait();
bail!("Export abgebrochen");
}
if child.try_wait()?.is_some() {
break;
}
std::thread::sleep(std::time::Duration::from_millis(10));
}
let result = child.wait_with_output()?;
ensure!(
result.status.success(),
"Finalisierung fehlgeschlagen: {}",
@@ -464,6 +513,12 @@ pub struct Export<'a> {
pub protected: &'a [PathBuf],
}
pub fn export(request: Export<'_>, cancelled: &dyn Fn() -> bool) -> Result<()> {
prepare_export(request, cancelled)?.publish(cancelled)
}
pub fn prepare_export(
request: Export<'_>,
cancelled: &dyn Fn() -> bool,
) -> Result<PreparedPublication> {
let mut b = template(request.template, request.target)?;
let mut protected = request.protected.to_vec();
protected.extend([
@@ -477,7 +532,7 @@ pub fn export(request: Export<'_>, cancelled: &dyn Fn() -> bool) -> Result<()> {
.iter()
.map(|s| PathBuf::from(&s.path)),
);
publish(
prepare_publication(
request.output,
request.overwrite,
&protected,
@@ -485,7 +540,7 @@ pub fn export(request: Export<'_>, cancelled: &dyn Fn() -> bool) -> Result<()> {
|temp| {
if request.target == Target::MacosArm64 {
fs::write(temp, &b)?;
codesign(temp, &["--remove-signature"])?;
codesign(temp, &["--remove-signature"], cancelled)?;
b = fs::read(temp)?;
}
append_payload(&mut b, request.module, request.target)?;
@@ -496,8 +551,12 @@ pub fn export(request: Export<'_>, cancelled: &dyn Fn() -> bool) -> Result<()> {
fs::set_permissions(temp, fs::Permissions::from_mode(0o755))?;
}
if request.target == Target::MacosArm64 {
codesign(temp, &["--force", "--sign", "-", "--timestamp=none"])?;
codesign(temp, &["--verify", "--strict"])?;
codesign(
temp,
&["--force", "--sign", "-", "--timestamp=none"],
cancelled,
)?;
codesign(temp, &["--verify", "--strict"], cancelled)?;
}
embedded(&fs::read(temp)?, request.target)?;
Ok(())