fix: panic on flatpak installations
This commit is contained in:
@@ -5,6 +5,7 @@ use gpui_updater::{EngineConfig, GitHubSource, UpdateStatus, Updater, Version};
|
||||
use instant::{Duration, Instant};
|
||||
|
||||
const COOP_UPDATE_EXPLANATION: &str = "COOP_UPDATE_EXPLANATION";
|
||||
const COOP_BUNDLE_TYPE: &str = "COOP_BUNDLE_TYPE";
|
||||
|
||||
fn get_github_repo_owner() -> String {
|
||||
std::env::var("COOP_GITHUB_REPO_OWNER").unwrap_or_else(|_| "reyakov".to_string())
|
||||
@@ -14,18 +15,27 @@ fn get_github_repo_name() -> String {
|
||||
std::env::var("COOP_GITHUB_REPO_NAME").unwrap_or_else(|_| "coop".to_string())
|
||||
}
|
||||
|
||||
fn is_flatpak_installation() -> bool {
|
||||
std::env::var("FLATPAK_ID").is_ok() || std::env::var(COOP_UPDATE_EXPLANATION).is_ok()
|
||||
/// Whether updates are managed by an external distribution channel
|
||||
/// (Flatpak/Snap), in which case the in-app updater must not run.
|
||||
fn uses_managed_updates() -> bool {
|
||||
// The Flatpak runtime exports `FLATPAK_ID` inside the sandbox.
|
||||
std::env::var("FLATPAK_ID").is_ok()
|
||||
// Allow opting out of in-app updates via an explicit environment variable.
|
||||
|| std::env::var(COOP_UPDATE_EXPLANATION).is_ok()
|
||||
// The Snap package sets `COOP_BUNDLE_TYPE=snap` (see snapcraft.yaml.in).
|
||||
|| std::env::var(COOP_BUNDLE_TYPE).is_ok_and(|value| value == "snap")
|
||||
}
|
||||
|
||||
/// Initialize the auto-update system.
|
||||
///
|
||||
/// Skips initialization when running as a Flatpak (updates are handled by the
|
||||
/// Flatpak distribution channel). Otherwise creates the global [`AutoUpdater`]
|
||||
/// Skips initialization when updates are handled by an external distribution
|
||||
/// channel (Flatpak/Snap). Otherwise creates the global [`AutoUpdater`]
|
||||
/// entity and schedules a check for updates after a 2-minute delay.
|
||||
pub fn init(window: &mut Window, cx: &mut App) {
|
||||
if is_flatpak_installation() {
|
||||
log::info!("Skipping auto-update initialization: App is installed via Flatpak");
|
||||
if uses_managed_updates() {
|
||||
log::info!(
|
||||
"Skipping auto-update initialization: App is installed via a managed distribution channel (Flatpak/Snap)"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -57,7 +67,27 @@ pub struct AutoUpdater {
|
||||
}
|
||||
|
||||
impl AutoUpdater {
|
||||
/// Whether auto-update is available for this installation.
|
||||
///
|
||||
/// Returns `false` on managed distribution channels (Flatpak/Snap), where
|
||||
/// updates are handled by the channel and no global updater is created.
|
||||
pub fn is_available(cx: &App) -> bool {
|
||||
cx.try_global::<GlobalAutoUpdater>().is_some()
|
||||
}
|
||||
|
||||
/// Retrieve the global auto updater instance, if one was initialized.
|
||||
pub fn try_global(cx: &App) -> Option<Entity<Self>> {
|
||||
cx.try_global::<GlobalAutoUpdater>()
|
||||
.map(|global| global.0.clone())
|
||||
}
|
||||
|
||||
/// Retrieve the global auto updater instance.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when auto-update is not available for this installation. Prefer
|
||||
/// [`AutoUpdater::try_global`] when the installation type is not known at
|
||||
/// compile time (e.g. Flatpak/Snap).
|
||||
pub fn global(cx: &App) -> Entity<Self> {
|
||||
cx.global::<GlobalAutoUpdater>().0.clone()
|
||||
}
|
||||
|
||||
@@ -380,7 +380,9 @@ impl Workspace {
|
||||
self.import_encryption(window, cx);
|
||||
}
|
||||
Command::Update => {
|
||||
let auto_updater = AutoUpdater::global(cx);
|
||||
// No-op on managed distribution channels (Flatpak/Snap) where
|
||||
// the in-app updater is never initialized.
|
||||
if let Some(auto_updater) = AutoUpdater::try_global(cx) {
|
||||
auto_updater.update(cx, |this, cx| {
|
||||
this.updater.update(cx, |updater, cx| {
|
||||
updater.check(cx);
|
||||
@@ -389,6 +391,7 @@ impl Workspace {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn confirm_reset_encryption(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
const ENC_MSG: &str = "Encryption Key is a special key that used to encrypt and decrypt your messages. \
|
||||
@@ -559,7 +562,7 @@ impl Workspace {
|
||||
.caret()
|
||||
.compact()
|
||||
.transparent()
|
||||
.dropdown_menu(move |this, _window, _cx| {
|
||||
.dropdown_menu(move |this, _window, cx| {
|
||||
let avatar = avatar.clone();
|
||||
let name = name.clone();
|
||||
|
||||
@@ -593,12 +596,15 @@ impl Workspace {
|
||||
IconName::Sun,
|
||||
Box::new(Command::ToggleTheme),
|
||||
)
|
||||
.separator()
|
||||
.menu_with_icon(
|
||||
// Only offer in-app updates when auto-update is
|
||||
// enabled (managed channels update themselves).
|
||||
.when(AutoUpdater::is_available(cx), |this| {
|
||||
this.separator().menu_with_icon(
|
||||
"Check for Updates",
|
||||
IconName::Device,
|
||||
Box::new(Command::Update),
|
||||
)
|
||||
})
|
||||
.menu_with_icon(
|
||||
"Settings",
|
||||
IconName::Settings,
|
||||
@@ -610,7 +616,6 @@ impl Workspace {
|
||||
}
|
||||
|
||||
fn titlebar_right(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let updater = AutoUpdater::global(cx);
|
||||
let chat = ChatRegistry::global(cx);
|
||||
let nip4e_enabled = AppSettings::get_nip4e(cx);
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
@@ -622,13 +627,19 @@ impl Workspace {
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let profile = persons.read(cx).get(&public_key, cx);
|
||||
let announcement = profile.announcement();
|
||||
let updater_idle = updater.read(cx).idle(cx);
|
||||
|
||||
// Update status is only shown when auto-update is available. On
|
||||
// managed distribution channels (Flatpak/Snap) no updater exists, so
|
||||
// nothing is rendered.
|
||||
let updater_status = AutoUpdater::try_global(cx).and_then(|updater| {
|
||||
let updater = updater.read(cx);
|
||||
(!updater.idle(cx)).then(|| updater.status(cx))
|
||||
});
|
||||
|
||||
h_flex()
|
||||
.when(!cx.theme().platform.is_mac(), |this| this.pr_2())
|
||||
.gap_2()
|
||||
.when(!updater_idle, |this| {
|
||||
let status = updater.read(cx).status(cx);
|
||||
.when_some(updater_status, |this, status| {
|
||||
this.child(div().text_xs().italic().child(status))
|
||||
})
|
||||
.when(nip4e_enabled, |this| {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# Snaps built by snapcraft without Snap Store credentials are unsigned and
|
||||
# cannot be installed without bypassing signature checks. Use:
|
||||
# sudo snap install --dangerous ./coop_<version>_<arch>.snap
|
||||
# For signed installs (`snap install coop`), publish via the Snap Store.
|
||||
name: coop
|
||||
title: Coop
|
||||
base: core24
|
||||
|
||||
@@ -35,3 +35,7 @@ SNAP_NAME="coop_${1}_${ARCH_SUFFIX}.snap"
|
||||
snapcraft --destructive-mode --output "$SNAP_NAME"
|
||||
|
||||
echo "Created snap package: $SNAP_NAME"
|
||||
echo ""
|
||||
echo "This snap is unsigned (built without Snap Store credentials)."
|
||||
echo "To install it locally, use:"
|
||||
echo " sudo snap install --dangerous ./$SNAP_NAME"
|
||||
|
||||
+16
-1
@@ -15,11 +15,26 @@ if [ "$#" -ne 1 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get system architecture (same mapping as script/bundle-snap)
|
||||
ARCH=$(uname -m)
|
||||
case "$ARCH" in
|
||||
x86_64) ARCH_SUFFIX="x86_64" ;;
|
||||
aarch64) ARCH_SUFFIX="aarch64" ;;
|
||||
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
|
||||
esac
|
||||
|
||||
snap_file="coop_${1}_${ARCH_SUFFIX}.snap"
|
||||
if [ ! -f "$snap_file" ]; then
|
||||
echo "Snap file not found: $snap_file"
|
||||
echo "Build it first with: script/bundle-snap $1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Rerun as root
|
||||
[ "$UID" -eq 0 ] || exec sudo bash -e "$0" "$@"
|
||||
|
||||
snap remove coop || true
|
||||
mkdir -p snap
|
||||
rm -rf snap/unpacked
|
||||
unsquashfs -dest snap/unpacked "coop_$1_amd64.snap"
|
||||
unsquashfs -dest snap/unpacked "$snap_file"
|
||||
snap try --classic snap/unpacked
|
||||
|
||||
Reference in New Issue
Block a user