mirror of
https://github.com/ivabus/gui
synced 2025-04-23 14:07:14 +03:00
* #433 install tea binary * #433 check tea cli * #433 install tea on install package --------- Co-authored-by: neil molina <neil@neils-MacBook-Pro.local>
This commit is contained in:
parent
7f7efdb79d
commit
e7bdad8027
10 changed files with 109 additions and 180 deletions
|
@ -15,6 +15,8 @@ import initializePushNotification, {
|
|||
syncPackageTopicSubscriptions
|
||||
} from "./libs/push-notification";
|
||||
|
||||
import init from "./libs/initialize";
|
||||
|
||||
log.info("App starting...");
|
||||
if (app.isPackaged) {
|
||||
Sentry.init({
|
||||
|
@ -31,6 +33,9 @@ if (app.isPackaged) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
const serveURL = serve({ directory: "." });
|
||||
const port = process.env.PORT || 3000;
|
||||
const allowDebug = !app.isPackaged || process.env.DEBUG_BUILD === "1";
|
||||
|
|
|
@ -1,44 +1,23 @@
|
|||
import { spawn, execSync } from "child_process";
|
||||
import { spawn, exec } from "child_process";
|
||||
import { clean } from "semver";
|
||||
import { getGuiPath } from "./tea-dir";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import axios from "axios";
|
||||
import initializeTeaCli from "./initialize";
|
||||
|
||||
import * as log from "electron-log";
|
||||
import { subscribeToPackageTopic } from "./push-notification";
|
||||
|
||||
const destinationDirectory = getGuiPath();
|
||||
|
||||
export const cliBinPath = path.join(destinationDirectory, "tea");
|
||||
|
||||
export async function installPackage(full_name: string) {
|
||||
return await new Promise((resolve, reject) => {
|
||||
let version = "";
|
||||
let lastError = "";
|
||||
const teaInstallation = spawn("/usr/local/bin/tea", [`+${full_name}`, "true"]);
|
||||
const teaVersion = await initializeTeaCli();
|
||||
|
||||
teaInstallation.stdout.on("data", (data) => {
|
||||
console.log("stdout:", data);
|
||||
});
|
||||
|
||||
teaInstallation.stderr.on("data", (err) => {
|
||||
lastError = err.toString();
|
||||
if (lastError && lastError.includes("installed") && lastError.includes(full_name)) {
|
||||
version = lastError.split("/").pop() || "";
|
||||
}
|
||||
});
|
||||
|
||||
teaInstallation.on("exit", async (code) => {
|
||||
if (code === 0) {
|
||||
try {
|
||||
await subscribeToPackageTopic(full_name);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
} finally {
|
||||
resolve({ version: clean(version) });
|
||||
}
|
||||
} else {
|
||||
reject(new Error(lastError));
|
||||
}
|
||||
});
|
||||
});
|
||||
if (!teaVersion) throw new Error("no tea");
|
||||
log.info(`installing package ${full_name}`);
|
||||
await asyncExec(`cd ${destinationDirectory} && ./tea +${full_name} true`);
|
||||
}
|
||||
|
||||
export async function openTerminal(cmd: string) {
|
||||
|
@ -75,19 +54,6 @@ export async function openTerminal(cmd: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function installTeaCli(version: string): Promise<string> {
|
||||
try {
|
||||
log.info("installing tea-cli");
|
||||
const command = 'TEA_YES=1 bash -c "sh <(curl https://tea.xyz)"';
|
||||
const output = execSync(command, { encoding: "utf-8" });
|
||||
log.info("tea-cli installed");
|
||||
return "success";
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
return error.message;
|
||||
}
|
||||
}
|
||||
|
||||
const createCommandScriptFile = async (cmd: string): Promise<string> => {
|
||||
try {
|
||||
const guiFolder = getGuiPath();
|
||||
|
@ -117,3 +83,17 @@ const createCommandScriptFile = async (cmd: string): Promise<string> => {
|
|||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
export async function asyncExec(cmd: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(cmd, (err, stdout) => {
|
||||
if (err) {
|
||||
console.log("err:", err);
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
console.log("stdout:", stdout);
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
41
modules/desktop/electron/libs/initialize.ts
Normal file
41
modules/desktop/electron/libs/initialize.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import fs from "fs";
|
||||
import { getGuiPath } from "./tea-dir";
|
||||
import * as log from "electron-log";
|
||||
import semver from "semver";
|
||||
import { cliBinPath, asyncExec } from "./cli";
|
||||
|
||||
const destinationDirectory = getGuiPath();
|
||||
|
||||
// TODO: move device_id generation here
|
||||
|
||||
// Get the binary path from the current app directory
|
||||
const binaryUrl = "https://tea.xyz/$(uname)/$(uname -m)";
|
||||
|
||||
export default async function initializeTeaCli(): Promise<string> {
|
||||
let version = "";
|
||||
let binCheck = "";
|
||||
// Create the destination directory if it doesn't exist
|
||||
if (!fs.existsSync(destinationDirectory)) {
|
||||
fs.mkdirSync(destinationDirectory, { recursive: true });
|
||||
}
|
||||
|
||||
const curlCommand = `curl -L -o "${cliBinPath}" "${binaryUrl}"`;
|
||||
|
||||
if (fs.existsSync(cliBinPath)) {
|
||||
log.info("binary tea already exists at", cliBinPath);
|
||||
binCheck = await asyncExec(`cd ${destinationDirectory} && ./tea --version`);
|
||||
} else {
|
||||
try {
|
||||
await asyncExec(curlCommand);
|
||||
log.info("Binary downloaded and saved to", cliBinPath);
|
||||
await asyncExec("chmod u+x " + cliBinPath);
|
||||
log.info("Binary is now ready for use at", cliBinPath);
|
||||
binCheck = await asyncExec(`cd ${destinationDirectory} && ./tea --version`);
|
||||
} catch (error) {
|
||||
log.error("Error setting-up tea binary:", error);
|
||||
}
|
||||
}
|
||||
version = binCheck.toString().split(" ")[1];
|
||||
log.info("binary tea version:", version);
|
||||
return semver.valid(version.trim()) ? version : "";
|
||||
}
|
|
@ -10,6 +10,8 @@ import path from "path";
|
|||
|
||||
import { installPackage, openTerminal, installTeaCli } from "./cli";
|
||||
|
||||
import initializeTeaCli from "./initialize";
|
||||
|
||||
import { getUpdater } from "./auto-updater";
|
||||
import fetch from "node-fetch";
|
||||
|
||||
|
@ -174,14 +176,18 @@ export default function initializeHandlers() {
|
|||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("install-tea-cli", async (_, data) => {
|
||||
ipcMain.handle("get-tea-version", async () => {
|
||||
try {
|
||||
log.info("installing tea cli");
|
||||
await installTeaCli(data.version);
|
||||
return "success";
|
||||
const version = await initializeTeaCli();
|
||||
if (!version) {
|
||||
throw new Error("failed to install tea cli");
|
||||
}
|
||||
|
||||
return { version, message: "" };
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
return error.message;
|
||||
return { version: "", message: error.message };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,92 +1,12 @@
|
|||
<script lang="ts">
|
||||
import Button from "@tea/ui/button/button.svelte";
|
||||
import { PackageStates, type GUIPackage } from "$libs/types";
|
||||
import { openTerminal, isPackageInstalled, installTeaCli } from '@native';
|
||||
import { packagesStore } from "$libs/stores";
|
||||
import clickOutside from "@tea/ui/lib/clickOutside";
|
||||
import ProgressBar from "@tea/ui/progress-bar/progress-bar.svelte";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { withFakeLoader } from "$libs/stores/pkgs";
|
||||
|
||||
const log = window.require("electron-log");
|
||||
|
||||
export let tea:GUIPackage|undefined;
|
||||
|
||||
let installing = false;
|
||||
let installProgress = 0;
|
||||
let message:string;
|
||||
let errorMessage = "";
|
||||
let fakeTimer: NodeJS.Timer;
|
||||
|
||||
let checkTeaPoll: NodeJS.Timer | null;
|
||||
const checkInstalled = async () => {
|
||||
if (checkTeaPoll) return;
|
||||
return new Promise((resolve) => {
|
||||
checkTeaPoll = setInterval(async () => {
|
||||
try {
|
||||
log.info("checking if tea is installed");
|
||||
const installed = await isPackageInstalled("tea.xyz", tea?.version);
|
||||
log.info("tea is installed:", installed);
|
||||
if (installed && checkTeaPoll) {
|
||||
clearInterval(checkTeaPoll);
|
||||
checkTeaPoll = null;
|
||||
resolve(null);
|
||||
close();
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
}
|
||||
}, 5000); // check every 5s
|
||||
})
|
||||
}
|
||||
import { navStore } from "$libs/stores"
|
||||
|
||||
const close = () => {
|
||||
packagesStore.requireTeaCli.set(false);
|
||||
navStore.showWelcome.set(false);
|
||||
}
|
||||
|
||||
const onOpenTerminal = async () => {
|
||||
if (installing) return;
|
||||
installing = true;
|
||||
try {
|
||||
openTerminal(`sh <(curl https://tea.xyz)`);
|
||||
await checkInstalled();
|
||||
packagesStore.updatePackage("tea.xyz", {
|
||||
state: PackageStates.INSTALLED,
|
||||
installed_versions: [tea?.version || "latest"]
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("install failed")
|
||||
} finally {
|
||||
installing = false;
|
||||
}
|
||||
}
|
||||
|
||||
const setupCli = async () => {
|
||||
installing = true;
|
||||
if (tea) {
|
||||
fakeTimer = withFakeLoader(tea, (p) => {
|
||||
installProgress = p;
|
||||
})
|
||||
try {
|
||||
message = await installTeaCli(tea.version);
|
||||
console.log("MESSAGE:", message)
|
||||
if (message != "success") {
|
||||
errorMessage = message;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("install failed cli", error)
|
||||
} finally {
|
||||
clearInterval(fakeTimer);
|
||||
installing = false;
|
||||
installProgress = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
setupCli();
|
||||
})
|
||||
</script>
|
||||
|
||||
<section class="fixed z-50 top-0 left-0 flex items-center justify-center">
|
||||
|
@ -99,29 +19,11 @@
|
|||
<h1 class="text-primary text-4xl mb-4">Welcome to the tea app!</h1>
|
||||
<p class="font-inter mb-4">This app is your gateway into the world of open-source software. Easily explore and manage packages with a click of a button. This app will notify you of any available software updates to ensure you’re safe and secure. Under the hood is the powerful tea cli.</p>
|
||||
|
||||
{#if !errorMessage}
|
||||
{#if installProgress != 100}
|
||||
<ProgressBar width={installProgress} />
|
||||
<p class="text-gray text-sm mt-2">initializing the app. please wait for a few seconds</p>
|
||||
{:else}
|
||||
<p class="text-gray text-sm mt-2">setup was succesfull!</p>
|
||||
<Button type="plain" color="secondary" class="w-7/12"
|
||||
onClick={() => close()}
|
||||
>
|
||||
EXPLORE OPEN-SOURCE
|
||||
</Button>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="h-10 w-7/12">
|
||||
<Button type="plain" color="secondary"
|
||||
loading={installing}
|
||||
onClick={onOpenTerminal}
|
||||
>
|
||||
INSTALL TEA CLI v{tea?tea.version:"latest"}
|
||||
</Button>
|
||||
</div>
|
||||
<p class="text-gray text-sm mt-2">tea cli is required in order to use our app. Clicking the link above will automatically install it via your command-line.</p>
|
||||
{/if}
|
||||
<Button type="plain" color="secondary" class="w-7/12"
|
||||
onClick={() => close()}
|
||||
>
|
||||
EXPLORE OPEN-SOURCE
|
||||
</Button>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
|
|
|
@ -252,7 +252,7 @@ export const writePackageCache = async (pkgs: Packages) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const installTeaCli = async (version: string): Promise<string> => {
|
||||
const res = await ipcRenderer.invoke("install-tea-cli", { version });
|
||||
return res;
|
||||
export const installTeaCli = async (): Promise<string> => {
|
||||
const res = await ipcRenderer.invoke("install-tea-cli");
|
||||
return res.version;
|
||||
};
|
||||
|
|
|
@ -371,6 +371,6 @@ export const writePackageCache = async (pkgs: Packages) => {
|
|||
console.log("write package cache", pkgs);
|
||||
};
|
||||
|
||||
export const installTeaCli = async (): Promise<string> => {
|
||||
return "success";
|
||||
export const installTeaCli = async (): Promise<{ version: string; message: string }> => {
|
||||
return { version: "0.0.0", message: "" };
|
||||
};
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
import { writable } from "svelte/store";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
import { installTeaCli } from "@native";
|
||||
const log = window.require("electron-log");
|
||||
|
||||
export default function initNavStore() {
|
||||
const sideNavOpen = writable<boolean>(false);
|
||||
const historyStore = writable<string[]>(["/"]);
|
||||
const showWelcome = writable<boolean>(false);
|
||||
|
||||
let history = ["/"];
|
||||
|
||||
historyStore.subscribe((v) => (history = v));
|
||||
|
@ -16,7 +21,14 @@ export default function initNavStore() {
|
|||
let isMovingNext = false;
|
||||
let isMovingBack = false;
|
||||
|
||||
installTeaCli().then((version: string) => {
|
||||
if (!version) {
|
||||
showWelcome.set(true);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
showWelcome,
|
||||
historyStore,
|
||||
sideNavOpen,
|
||||
prevPath: prevPathStore,
|
||||
|
|
|
@ -31,8 +31,6 @@ export default function initPackagesStore() {
|
|||
const packageMap = writable<Packages>({ version: "0", packages: {} });
|
||||
const packageList = derived(packageMap, ($packages) => Object.values($packages.packages));
|
||||
|
||||
const requireTeaCli = writable<boolean>(false);
|
||||
|
||||
let packagesIndex: Fuse<GUIPackage>;
|
||||
|
||||
const updateAllPackages = (guiPkgs: GUIPackage[]) => {
|
||||
|
@ -86,16 +84,6 @@ To read more about this package go to [${guiPkg.homepage}](${guiPkg.homepage}).
|
|||
updatePackage(guiPkg.full_name!, updatedPackage);
|
||||
};
|
||||
|
||||
const checkTeaCLIPackage = async (teaPkg: Package, installedPkg?: InstalledPackage) => {
|
||||
if (!installedPkg) {
|
||||
requireTeaCli.set(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const isUpToDate = teaPkg.version === installedPkg?.installed_versions[0];
|
||||
log.info("check if Tea-CLI is up to date:", isUpToDate);
|
||||
};
|
||||
|
||||
const init = async function () {
|
||||
log.info("packages store: try initialize");
|
||||
|
||||
|
@ -124,11 +112,6 @@ To read more about this package go to [${guiPkg.homepage}](${guiPkg.homepage}).
|
|||
try {
|
||||
const installedPkgs: InstalledPackage[] = await getInstalledPackages();
|
||||
|
||||
log.info("sync test for tea-cli");
|
||||
const distTea = pkgs.find((p) => p.full_name === "tea.xyz");
|
||||
const installedTeaPkg = installedPkgs.find((p) => p.full_name === "tea.xyz");
|
||||
if (distTea) await checkTeaCLIPackage(distTea, installedTeaPkg);
|
||||
|
||||
log.info("set NEEDS_UPDATE state to pkgs");
|
||||
for (const [i, iPkg] of installedPkgs.entries()) {
|
||||
const pkg = guiPkgs.find((p) => p.full_name === iPkg.full_name);
|
||||
|
@ -211,7 +194,6 @@ To read more about this package go to [${guiPkg.homepage}](${guiPkg.homepage}).
|
|||
return {
|
||||
packageList,
|
||||
syncProgress,
|
||||
requireTeaCli,
|
||||
search: async (term: string, limit = 5): Promise<GUIPackage[]> => {
|
||||
if (!term || !packagesIndex) return [];
|
||||
// TODO: if online, use algolia else use Fuse
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import { page } from '$app/stores';
|
||||
import { t } from '$libs/translations';
|
||||
import { afterNavigate } from '$app/navigation';
|
||||
import { packagesStore, notificationStore } from '$libs/stores';
|
||||
import { packagesStore, navStore } from '$libs/stores';
|
||||
import Packages from '$components/packages/packages.svelte';
|
||||
import { PackageStates, SideMenuOptions, type GUIPackage } from '$libs/types';
|
||||
// import SortingButtons from "$components/search-packages/sorting-buttons.svelte";
|
||||
|
@ -16,7 +16,8 @@
|
|||
|
||||
const log = window.require("electron-log");
|
||||
|
||||
const { packageList, requireTeaCli } = packagesStore;
|
||||
const { packageList } = packagesStore;
|
||||
const { showWelcome } = navStore;
|
||||
|
||||
const url = $page.url;
|
||||
|
||||
|
@ -99,8 +100,8 @@
|
|||
</div>
|
||||
|
||||
<SideMenu bind:activeOption={sideMenuOption}/>
|
||||
{#if $requireTeaCli && teaPkg }
|
||||
<WelcomeModal tea={teaPkg} />
|
||||
{#if $showWelcome }
|
||||
<WelcomeModal />
|
||||
{/if}
|
||||
<style>
|
||||
#content {
|
||||
|
|
Loading…
Reference in a new issue