fix: preserve repository ownership and Flatpak update results

This commit is contained in:
Dylan M. Taylor committed 2026-11-02 17:37:36 -05:00
1 parent 25e53ccbba
commit a4e76179cc
5 files changed
+44 -12

No files matched your search

-7
View File
@@ -379,13 +379,6 @@ impl NativeBackend {
// One installer call can cover several apps, and fail after some of them went in.
let latest = shared.latest.lock().unwrap_or_else(PoisonError::into_inner).get(app.id).cloned();
let result = match (result, latest) {
(Ok(_), _)
if existing.as_ref().is_some_and(|e| e.externally_managed)
&& found.is_some()
&& existing.as_ref().and_then(|e| e.version.as_ref()) == found.as_ref().and_then(|f| f.version.as_ref()) =>
{
Ok(format!("{} is already up to date", app.name))
}
(Err(e), Some(release))
if !uninstall && release.version.as_ref().is_some_and(|v| landed_despite(&e, existing.as_ref(), found.as_ref(), v)) =>
{
+3 -3
View File
@@ -620,7 +620,7 @@ pub mod linux {
// A Flatpak runs under the id it was found under, which may be an older one.
let location = if p.manager == InstallSource::Flatpak { p.name.clone() } else { binary_on_path(app.id) };
let externally_managed = p.manager == InstallSource::Flatpak
&& p.origin.as_deref().is_some_and(|origin| {
&& p.origin.as_deref().is_none_or(|origin| {
if packages::is_flatpark_origin(origin) {
return true;
}
@@ -629,8 +629,8 @@ pub mod linux {
Some("system") | None => "--system".to_string(),
Some(name) => format!("--installation={name}"),
};
crate::process::capture_flatpak(&["remotes", &scope, "--columns=name,url"], Duration::from_secs(10))
.is_some_and(|remotes| packages::is_artcraft_remote(origin, &remotes))
crate::process::capture_flatpak(&["remotes", &scope, "--show-disabled", "--columns=name,url"], Duration::from_secs(10))
.is_none_or(|remotes| packages::remote_manages_flatpak(origin, &remotes))
});
return Some(Installed {
version: packages::version_of(p),
+7 -2
View File
@@ -121,6 +121,11 @@ pub fn install_at(
{
return Err(format!("{} is running. Quit it, then try again.", app.name));
}
if kind == PackageKind::Flatpak
&& let Some(installed) = existing.filter(|installed| installed.source == InstallSource::Flatpak && installed.externally_managed)
{
return Err(managed_elsewhere(app, installed, false).unwrap_or_else(|| "Update this app through its Flatpak repository".into()));
}
bind_artifact_version(app, release, asset)?;
// A feed can't send an app back to an older build; only a version the person chose by name
// (a downgrade with a way back if it fails) gets past this.
@@ -1262,7 +1267,7 @@ pub(crate) mod linux {
if !reference.starts_with(&format!("app/{id}/")) || !reference.ends_with("/stable") {
return Ok(false);
}
let remotes = run(&["remotes", "--columns=name,url"])?;
let remotes = run(&["remotes", "--show-disabled", "--columns=name,url"])?;
let remote = remotes.lines().find_map(|line| {
let (name, _) = line.split_once('\t')?;
crate::packages::is_artcraft_remote(name, line).then(|| name.to_string())
@@ -1274,7 +1279,7 @@ pub(crate) mod linux {
return Err("The artcraft-oci remote points to a different repository".into());
}
run(&["remote-add", "--noninteractive", "artcraft-oci", "https://getartcraft.com/artcraft-oci.flatpakrepo"])?;
let remotes = run(&["remotes", "--columns=name,url"])?;
let remotes = run(&["remotes", "--show-disabled", "--columns=name,url"])?;
if !crate::packages::is_artcraft_remote("artcraft-oci", &remotes) {
return Err("The ArtCraft OCI descriptor configured an unexpected repository".into());
}
+28
View File
@@ -305,3 +305,31 @@ mod migration_tests {
}
}
}
/// Only a confirmed bundle origin with no repository URL permits bundle replacement.
/// Missing rows are unknown, so preserve remote ownership conservatively.
pub fn remote_manages_flatpak(origin: &str, remotes: &str) -> bool {
remotes
.lines()
.find_map(|line| {
let mut fields = line.split('\t');
if fields.next().map(str::trim) != Some(origin) {
return None;
}
Some(fields.next().is_some_and(|url| !url.trim().is_empty()))
})
.unwrap_or(true)
}
#[cfg(test)]
mod remote_ownership_tests {
use super::*;
#[test]
fn only_confirmed_empty_urls_allow_bundle_replacement() {
assert!(!remote_manages_flatpak("app-origin", "app-origin\t\n"));
assert!(remote_manages_flatpak("renamed", "renamed\thttps://example.org/repo\n"));
assert!(remote_manages_flatpak("renamed", "renamed\toci+https://getartcraft.com\n"));
assert!(remote_manages_flatpak("missing", "other\t\n"));
assert!(!remote_manages_flatpak("app-origin", "app-origin"));
}
}
+6
View File
@@ -797,7 +797,13 @@ pub(crate) fn cancellable_output(mut cmd: Command, check: impl Fn() -> Result<()
let mut child =
cmd.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().map_err(|e| format!("couldn't run flatpak ({e})"))?;
let (stdout, stderr) = (read_all(child.stdout.take()), read_all(child.stderr.take()));
let started = Instant::now();
let status = loop {
if started.elapsed() >= Duration::from_secs(60 * 60) {
let _ = child.kill();
let _ = child.wait();
return Err("Flatpak did not finish within one hour".into());
}
if let Err(e) = check() {
let _ = child.kill();
let _ = child.wait();