Skip to content

Commit

Permalink
Merge pull request #1115 from 89luca89/feat/flatpak_support
Browse files Browse the repository at this point in the history
fix(build): add flatpak support
  • Loading branch information
89luca89 authored Jun 12, 2024
2 parents 4d68996 + 0b10813 commit 4e87d5b
Show file tree
Hide file tree
Showing 11 changed files with 431 additions and 102 deletions.
239 changes: 229 additions & 10 deletions desktop/src-tauri/Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ serde_qs = "0.12.0"
serde_yaml = "0.9.21"
serde_urlencoded = "0.7.1"
tauri-plugin-deep-link = { version = "0.1.0" }
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
# Logging
log = { version = "0.4" }
tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
# Datetime
chrono = { version = "0.4.23", features = ["serde"] }

Expand Down
53 changes: 32 additions & 21 deletions desktop/src-tauri/src/custom_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use crate::ui_messages::{
use crate::AppState;
use log::{error, info};
use serde::{Deserialize, Serialize};
use std::env;
use std::path::Path;
use tauri::{AppHandle, Manager, State};
use thiserror::Error;
use url::Url;
Expand Down Expand Up @@ -201,28 +203,37 @@ impl CustomProtocol {
match result {
Ok(..) => {}
Err(error) => {
let msg = "Either update-desktop-database or xdg-mime are missing. Please make sure they are available on your system";
log::warn!("Custom protocol setup failed; {}: {}", msg, error);

tauri::async_runtime::block_on(async {
let app_state = app.state::<AppState>();
let show_toast_msg = ShowToastMsg::new(
"Custom protocol handling needs to be configured".to_string(),
msg.to_string(),
ToastStatus::Warning,
);
if let Err(err) = app_state
.ui_messages
.send(UiMessage::ShowToast(show_toast_msg))
.await
{
log::error!(
"Failed to broadcast show toast message: {:?}, {}",
err.0,
err
let mut is_flatpak = false;

match env::var("FLATPAK_ID") {
Ok(_) => is_flatpak = true,
Err(_) => is_flatpak = false,
}

if !is_flatpak {
let msg = "Either update-desktop-database or xdg-mime are missing. Please make sure they are available on your system";
log::warn!("Custom protocol setup failed; {}: {}", msg, error);

tauri::async_runtime::block_on(async {
let app_state = app.state::<AppState>();
let show_toast_msg = ShowToastMsg::new(
"Custom protocol handling needs to be configured".to_string(),
msg.to_string(),
ToastStatus::Warning,
);
};
})
if let Err(err) = app_state
.ui_messages
.send(UiMessage::ShowToast(show_toast_msg))
.await
{
log::error!(
"Failed to broadcast show toast message: {:?}, {}",
err.0,
err
);
};
})
}
}
};
}
Expand Down
9 changes: 9 additions & 0 deletions desktop/src-tauri/src/file_exists.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use crate::{commands::DevpodCommandError, AppState, UiMessage};
use log::{error, info, warn};
use std::path::Path;

#[tauri::command]
pub fn file_exists(filepath: &str) -> bool {
info!("finding file in {}", filepath);
return Path::new(&filepath).exists();
}
7 changes: 7 additions & 0 deletions desktop/src-tauri/src/get_env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use crate::{commands::DevpodCommandError, AppState, UiMessage};
use log::error;

#[tauri::command]
pub fn get_env(name: &str) -> String {
std::env::var(String::from(name)).unwrap_or(String::from(""))
}
58 changes: 43 additions & 15 deletions desktop/src-tauri/src/install_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ fn install(_app_handle: AppHandle, force: bool) -> Result<(), InstallCLIError> {
let mut user_local_bin = home;
user_local_bin.push(".local/bin/devpod");

target_paths.push(user_bin);
target_paths.push(user_local_bin);
target_paths.push(user_bin);
}

let mut latest_error: Option<InstallCLIError> = None;
Expand Down Expand Up @@ -124,21 +124,49 @@ fn install(_app_handle: AppHandle, force: bool) -> Result<(), InstallCLIError> {
target_path.to_string_lossy()
);

let operation = if is_on_tmpfs { copy } else { symlink };
match operation(cli_path.clone(), &target_path)
.with_context(|| format!("path: {}", str_target_path))
.map_err(InstallCLIError::Link)
{
Ok(..) => {
return Ok(());
let mut is_flatpak = false;

match env::var("FLATPAK_ID") {
Ok(_) => is_flatpak = true,
Err(_) => is_flatpak = false,
}

if is_flatpak {
match copy(cli_path.clone(), &target_path)
.with_context(|| format!("path: {}", str_target_path))
.map_err(InstallCLIError::Link)
{
Ok(..) => {
return Ok(());
}
Err(err) => {
warn!(
"Failed to copy from {} to {}: {}; Retrying with other paths...",
cli_path.to_string_lossy(),
target_path.to_string_lossy(),
err
);
latest_error = Some(err);
}
}
Err(err) => {
warn!(
"Failed to link to {}: {}; Retrying with other paths...",
target_path.to_string_lossy(),
err
);
latest_error = Some(err);
} else {
let operation = if is_on_tmpfs { copy } else { symlink };

match operation(cli_path.clone(), &target_path)
.with_context(|| format!("path: {}", str_target_path))
.map_err(InstallCLIError::Link)
{
Ok(..) => {
return Ok(());
}
Err(err) => {
warn!(
"Failed to link to {}: {}; Retrying with other paths...",
target_path.to_string_lossy(),
err
);
latest_error = Some(err);
}
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions desktop/src-tauri/src/log_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use crate::{commands::DevpodCommandError, AppState, UiMessage};
use log::{error, info, warn};

#[tauri::command]
pub fn log_message(message: String) {
info!("logging message: {}", message);
}
9 changes: 9 additions & 0 deletions desktop/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ mod action_logs;
mod commands;
mod community_contributions;
mod custom_protocol;
mod file_exists;
mod fix_env;
mod get_env;
mod install_cli;
mod log_message;
mod logging;
mod providers;
mod server;
Expand Down Expand Up @@ -147,6 +150,9 @@ fn main() -> anyhow::Result<()> {
action_logs::get_action_log_file,
action_logs::sync_action_logs,
install_cli::install_cli,
get_env::get_env,
file_exists::file_exists,
log_message::log_message,
community_contributions::get_contributions,
updates::get_pending_update,
updates::check_updates
Expand All @@ -161,6 +167,9 @@ fn main() -> anyhow::Result<()> {
action_logs::get_action_log_file,
action_logs::sync_action_logs,
install_cli::install_cli,
get_env::get_env,
file_exists::file_exists,
log_message::log_message,
community_contributions::get_contributions,
]);
}
Expand Down
6 changes: 6 additions & 0 deletions desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
"sidecar": true,
"args": true
},
{
"name": "run-path-devpod-wrapper",
"cmd": "/app/bin/devpod-cli",
"args": true
},
{
"name": "run-path-devpod-cli",
"cmd": "devpod",
Expand All @@ -46,6 +51,7 @@
},
"fs": {
"scope": [
"$HOME/**",
"$APPDATA/*",
"$APPDATA/actions/*"
],
Expand Down
111 changes: 63 additions & 48 deletions desktop/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,40 +30,40 @@ import { Command as DevPodCommand } from "./command"
type TChannels = {
event:
| Readonly<{
type: "ShowToast"
message: string
title: string
status: NonNullable<UseToastOptions["status"]>
}>
type: "ShowToast"
message: string
title: string
status: NonNullable<UseToastOptions["status"]>
}>
| Readonly<{ type: "ShowDashboard" }>
| Readonly<{ type: "CommandFailed" }>
| Readonly<{
type: "OpenWorkspace"
workspace_id: string | null
provider_id: string | null
ide: string | null
source: string
}>
| Readonly<{
type: "ImportWorkspace"
workspace_id: string
workspace_uid: string
devpod_pro_host: string
project: string
options: Record<string, string> | null
}>
| Readonly<{
type: "SetupPro"
host: string
accessKey: string | null
options: Record<string, string> | null
}>
| Readonly<{ type: "CommandFailed" }>
| Readonly<{
type: "OpenWorkspace"
workspace_id: string | null
provider_id: string | null
ide: string | null
source: string
}>
| Readonly<{
type: "ImportWorkspace"
workspace_id: string
workspace_uid: string
devpod_pro_host: string
project: string
options: Record<string, string> | null
}>
| Readonly<{
type: "SetupPro"
host: string
accessKey: string | null
options: Record<string, string> | null
}>
}
type TChannelName = keyof TChannels
type TClientEventListener<TChannel extends TChannelName> = (payload: TChannels[TChannel]) => void
type TClientSettings = Pick<
TSettings,
"debugFlag" | "additionalCliFlags" | "dotfilesURL" | "additionalEnvVars"
TSettings,
"debugFlag" | "additionalCliFlags" | "dotfilesURL" | "additionalEnvVars"
>
export type TPlatform = Awaited<ReturnType<typeof os.platform>>
export type TArch = Awaited<ReturnType<typeof os.arch>>
Expand Down Expand Up @@ -225,8 +225,23 @@ class Client {
return Return.Failed("Unable to install CLI")
}
}

public async getEnv(name: string) {
return await invoke("get_env", { name });
}

public async isCLIInstalled(): Promise<Result<boolean>> {
try {
// we're in a flatpak, we need to check in other paths.
const isflatpak = await this.getEnv("FLATPAK_ID");
if (isflatpak) {
const home_dir = await this.getEnv("HOME");
// this will throw if doesn't exist
const exists = await invoke("file_exists", {filepath: home_dir+"/.local/bin/devpod" }) as unknown as boolean;

return Return.Value(exists)
}

const result = await new Command("run-path-devpod-cli", ["version"]).execute()
if (result.code !== 0) {
return Return.Value(false)
Expand Down Expand Up @@ -288,25 +303,25 @@ class Client {
// Synchronize promise state with update operation
await new Promise((res, rej) => {
updater
.onUpdaterEvent((event) => {
if (event.status === "ERROR") {
unsubscribe?.()
rej(event.error)

return
}

if (event.status === "DONE") {
unsubscribe?.()
res(undefined)

return
}
})
.then(async (u) => {
unsubscribe = u
await updater.installUpdate()
})
.onUpdaterEvent((event) => {
if (event.status === "ERROR") {
unsubscribe?.()
rej(event.error)

return
}

if (event.status === "DONE") {
unsubscribe?.()
res(undefined)

return
}
})
.then(async (u) => {
unsubscribe = u
await updater.installUpdate()
})
})

return Return.Ok()
Expand Down
Loading

0 comments on commit 4e87d5b

Please sign in to comment.