Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

Commit 1d282ac

Browse files
authored
show version correctly (#544)
* show version correctly * fix lint/prettier * button style
1 parent 6c8bc75 commit 1d282ac

23 files changed

Lines changed: 567 additions & 77 deletions

modules/desktop/electron/libs/initialize.ts

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import fs from "fs";
22
import { getGuiPath } from "./tea-dir";
33
import log from "./logger";
4-
import semver from "semver";
54
import { cliBinPath, asyncExec } from "./cli";
65
import { createInitialSessionFile } from "./auth";
7-
import semverCompare from "semver/functions/compare";
6+
import { SemVer, isValidSemVer } from "@tea/libtea";
87

9-
// Versions before this do not support the --json flag
10-
const MINIMUM_TEA_VERSION = "0.28.3";
8+
const MINIMUM_TEA_VERSION = "0.31.2";
119

1210
const destinationDirectory = getGuiPath();
1311

@@ -35,55 +33,46 @@ export async function initializeTeaCli(): Promise<string> {
3533
}
3634

3735
async function initializeTeaCliInternal(): Promise<string> {
38-
try {
39-
let binCheck = "";
40-
let needsUpdate = false;
36+
let binCheck = "";
37+
let needsUpdate = false;
4138

42-
// Create the destination directory if it doesn't exist
43-
if (!fs.existsSync(destinationDirectory)) {
44-
fs.mkdirSync(destinationDirectory, { recursive: true });
45-
}
39+
// Create the destination directory if it doesn't exist
40+
if (!fs.existsSync(destinationDirectory)) {
41+
fs.mkdirSync(destinationDirectory, { recursive: true });
42+
}
4643

47-
// replace this with max's pr
48-
const curlCommand = `curl --insecure -L -o "${cliBinPath}" "${binaryUrl}"`;
49-
50-
const exists = fs.existsSync(cliBinPath);
51-
if (exists) {
52-
log.info("binary tea already exists at", cliBinPath);
53-
try {
54-
binCheck = await asyncExec(`cd ${destinationDirectory} && ./tea --version`);
55-
const teaVersion = binCheck.toString().split(" ")[1];
56-
if (semverCompare(teaVersion, MINIMUM_TEA_VERSION) < 0) {
57-
log.info("binary tea version is too old, updating");
58-
needsUpdate = true;
59-
}
60-
} catch (error) {
61-
// probably binary is not executable or no permission
62-
log.error("Error checking tea binary version:", error);
44+
// replace this with max's pr
45+
const curlCommand = `curl --insecure -L -o "${cliBinPath}" "${binaryUrl}"`;
46+
47+
const exists = fs.existsSync(cliBinPath);
48+
if (exists) {
49+
log.info("binary tea already exists at", cliBinPath);
50+
try {
51+
binCheck = await asyncExec(`cd ${destinationDirectory} && ./tea --version`);
52+
const teaVersion = binCheck.toString().split(" ")[1].trim();
53+
if (new SemVer(teaVersion).compare(new SemVer(MINIMUM_TEA_VERSION)) < 0) {
54+
log.info("binary tea version is too old, updating");
6355
needsUpdate = true;
64-
await asyncExec(`cd ${destinationDirectory} && rm tea`);
65-
}
66-
}
67-
68-
if (!exists || needsUpdate) {
69-
try {
70-
await asyncExec(curlCommand);
71-
log.info("Binary downloaded and saved to", cliBinPath);
72-
await asyncExec("chmod u+x " + cliBinPath);
73-
log.info("Binary is now ready for use at", cliBinPath);
74-
binCheck = await asyncExec(`cd ${destinationDirectory} && ./tea --version`);
75-
} catch (error) {
76-
log.error("Error setting-up tea binary:", error);
7756
}
57+
} catch (error) {
58+
// probably binary is not executable or no permission
59+
log.error("Error checking tea binary version:", error);
60+
needsUpdate = true;
61+
await asyncExec(`cd ${destinationDirectory} && rm tea`);
7862
}
63+
}
7964

80-
const version = binCheck.toString().split(" ")[1];
81-
log.info("binary tea version:", version);
82-
return semver.valid(version.trim()) ? version : "";
83-
} catch (error) {
84-
log.error(error);
85-
return "";
65+
if (!exists || needsUpdate) {
66+
await asyncExec(curlCommand);
67+
log.info("Binary downloaded and saved to", cliBinPath);
68+
await asyncExec("chmod u+x " + cliBinPath);
69+
log.info("Binary is now ready for use at", cliBinPath);
70+
binCheck = await asyncExec(`cd ${destinationDirectory} && ./tea --version`);
8671
}
72+
73+
const version = binCheck.toString().split(" ")[1];
74+
log.info("binary tea version:", version);
75+
return isValidSemVer(version.trim()) ? version : "";
8776
}
8877

8978
export default async function initialize(): Promise<string> {

modules/desktop/electron/libs/tea-dir.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22
import fs from "fs";
33
import path from "path";
44
import { app } from "electron";
5-
import semver, { SemVer } from "semver";
65
import log from "./logger";
76
import type { InstalledPackage } from "../../src/libs/types";
8-
import semverCompare from "semver/functions/compare";
97
import { mkdirp } from "mkdirp";
108
import fetch from "node-fetch";
9+
import { SemVer, isValidSemVer } from "@tea/libtea";
1110

1211
type Dir = {
1312
name: string;
@@ -34,26 +33,26 @@ export async function getInstalledPackages(): Promise<InstalledPackage[]> {
3433
log.info("recursively reading:", pkgsPath);
3534
const folders = await deepReadDir({
3635
dir: pkgsPath,
37-
continueDeeper: (name: string) => !semver.valid(name) && name !== ".tea",
38-
filter: (name: string) => !!semver.valid(name) && name !== ".tea"
36+
continueDeeper: (name: string) => !isValidSemVer(name) && name !== ".tea",
37+
filter: (name: string) => !!isValidSemVer(name) && name !== ".tea"
3938
});
4039

4140
const bottles = folders
4241
.map((p: string) => p.split(".tea/")[1])
4342
.map(parseVersionFromPath)
4443
.filter((v): v is ParsedVersion => !!v)
45-
.sort((a, b) => semverCompare(b.semVer, a.semVer));
44+
.sort((a, b) => b.semVer.compare(a.semVer));
4645

4746
log.info("installed bottles:", bottles.length);
4847

4948
return bottles.reduce<InstalledPackage[]>((pkgs, bottle) => {
5049
const pkg = pkgs.find((v) => v.full_name === bottle.full_name);
5150
if (pkg) {
52-
pkg.installed_versions.push(bottle.semVer.version);
51+
pkg.installed_versions.push(bottle.semVer.toString());
5352
} else {
5453
pkgs.push({
5554
full_name: bottle.full_name,
56-
installed_versions: [bottle.semVer.version]
55+
installed_versions: [bottle.semVer.toString()]
5756
});
5857
}
5958
return pkgs;
@@ -65,7 +64,7 @@ const parseVersionFromPath = (versionPath: string): ParsedVersion | null => {
6564
const path = versionPath.trim().split("/");
6665
const version = path.pop();
6766
return {
68-
semVer: new SemVer(semver.clean(version || "") || ""),
67+
semVer: new SemVer(version ?? ""),
6968
full_name: path.join("/")
7069
};
7170
} catch (e) {
@@ -119,9 +118,9 @@ export const deepReadDir = async ({
119118
if (f.isDirectory() && deeper) {
120119
const nextFiles = await deepReadDir({ dir: nextPath, continueDeeper, filter });
121120
arrayOfFiles.push(...nextFiles);
122-
} else if (filter && filter(f.name)) {
121+
} else if (!f.isSymbolicLink() && filter && filter(f.name)) {
123122
arrayOfFiles.push(nextPath);
124-
} else if (!filter) {
123+
} else if (!f.isSymbolicLink() && !filter) {
125124
arrayOfFiles.push(nextPath);
126125
}
127126
}

modules/desktop/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@sveltejs/adapter-static": "^1.0.0-next.48",
4040
"@sveltejs/kit": "^1.15.9",
4141
"@tea/ui": "workspace:*",
42+
"@tea/libtea": "workspace:*",
4243
"@testing-library/jest-dom": "^5.16.5",
4344
"@testing-library/svelte": "^3.2.2",
4445
"@types/bcryptjs": "^2.4.2",
@@ -103,7 +104,6 @@
103104
"mousetrap": "^1.6.5",
104105
"pushy-electron": "^1.0.11",
105106
"renderer": "link:@types/electron/renderer",
106-
"semver": "^7.3.8",
107107
"svelte-infinite-scroll": "^2.0.1",
108108
"svelte-markdown": "^0.2.3",
109109
"svelte-watch-resize": "^1.0.3",
@@ -114,7 +114,8 @@
114114
},
115115
"pnpm": {
116116
"onlyBuiltDependencies": [
117-
"@tea/ui"
117+
"@tea/ui",
118+
"@tea/libtea"
118119
]
119120
},
120121
"homepage": "https://tea.xyz",

modules/desktop/src/components/package-banner/package-banner.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import Button from "@tea/ui/button/button.svelte";
66
import ButtonIcon from "@tea/ui/button-icon/button-icon.svelte";
77
import ToolTip from "@tea/ui/tool-tip/tool-tip.svelte";
8-
import semverCompare from "semver/functions/compare";
98
import ProgressCircle from "@tea/ui/progress-circle/progress-circle.svelte";
109
1110
import type { GUIPackage } from "$libs/types";
@@ -16,6 +15,7 @@
1615
import PackageImage from "../package-card/bg-image.svelte";
1716
import PackageVersionSelector from "$components/package-install-button/package-version-selector.svelte";
1817
import { fixPackageName } from "$libs/packages/pkg-utils";
18+
import { semverCompare } from "$libs/packages/pkg-utils";
1919
2020
export let pkg: GUIPackage;
2121
let installing = false;

modules/desktop/src/components/package-card/package-card.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
<div class="flex w-fit flex-col items-center">
5858
<div class="install-button {layout}" on:mousedown={preventPropagation}>
5959
{#if pkg.state === PackageStates.INSTALLED}
60-
<PackageInstalledBadge version={pkg.version} />
60+
<PackageInstalledBadge {pkg} />
6161
{:else}
6262
<PackageInstallButton
6363
{pkg}

modules/desktop/src/components/package-install-button/package-install-button.svelte

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@
2424
return state === PackageStates.INSTALLING || state === PackageStates.UPDATING;
2525
};
2626
27+
const getVersion = (pkg: GUIPackage) => {
28+
if (pkg.state === PackageStates.INSTALLED) {
29+
return pkg.installed_versions?.[0] ?? pkg.version;
30+
}
31+
return pkg.version;
32+
};
33+
2734
const badgeClass: Record<PackageStates, string> = {
2835
[PackageStates.AVAILABLE]: "install-badge",
2936
[PackageStates.INSTALLING]: "install-badge",
@@ -53,7 +60,7 @@
5360
>
5461
<div class="flex items-center gap-x-2">
5562
<div>{ctaLabel}</div>
56-
<div class="version-label {badgeClass[pkg.state]}">{pkg.version}</div>
63+
<div class="version-label {badgeClass[pkg.state]}">v{getVersion(pkg)}</div>
5764
</div>
5865
{#if hasVersionSelectorDropdown}
5966
<i class="icon-downward-arrow flex" />

modules/desktop/src/components/package-install-button/package-installed-badge.svelte

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
<script lang="ts">
2-
export let version = "1.0.0";
2+
import type { GUIPackage } from "$libs/types";
3+
4+
export let pkg: GUIPackage | null = null;
35
</script>
46

57
<div class="container relative h-full">
68
<div class="content flex items-center justify-center gap-2 p-2">
79
<i class="icon-check-circle-o flex text-sm text-[#00ffd0]" />
810
<div class="text-xs">INSTALLED</div>
911
<div class="rounded-sm bg-white px-1 text-[10px] leading-[12px] text-black">
10-
v{version}
12+
v{pkg?.installed_versions?.[0]}
1113
</div>
1214
</div>
1315
</div>

modules/desktop/src/components/package-install-button/package-version-selector.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { PackageStates, type GUIPackage } from "$libs/types";
33
import clickOutside from "@tea/ui/lib/clickOutside";
44
import PackageStateButton from "./package-install-button.svelte";
5-
import semver from "semver";
5+
import { semverCompare } from "$libs/packages/pkg-utils";
66
77
export let buttonSize: "small" | "large" = "small";
88
export let pkg: GUIPackage;
@@ -27,7 +27,7 @@
2727
2828
$: installedVersions = pkg.installed_versions || [];
2929
$: allVersions = Array.from(new Set([pkg.version, ...availableVersions])).sort(
30-
(a: string, b: string) => semver.rcompare(a, b)
30+
(a: string, b: string) => semverCompare(b, a)
3131
);
3232
3333
const handleClick = (evt: MouseEvent, version: string) => {

modules/desktop/src/libs/packages/pkg-utils.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import log from "$libs/logger";
12
import type { GUIPackage } from "$libs/types";
2-
import { clean } from "semver";
3-
import semverCompare from "semver/functions/compare";
3+
import { SemVer } from "@tea/libtea";
44

55
// Find a list of available versions for a package based on the bottles
66
export const findAvailableVersions = (pkg: Pick<GUIPackage, "bottles" | "version">) => {
@@ -15,10 +15,19 @@ export const findAvailableVersions = (pkg: Pick<GUIPackage, "bottles" | "version
1515
if (b.arch === arch) versionSet.add(b.version);
1616
}
1717

18-
return Array.from(versionSet).sort((a, b) => semverCompare(cleanVersion(b), cleanVersion(a)));
18+
return Array.from(versionSet).sort((a, b) => semverCompare(b, a));
1919
};
2020

21-
export const cleanVersion = (version: string) => clean(version) || "0.0.0";
21+
export const semverCompare = (a: string, b: string) => {
22+
try {
23+
return new SemVer(a).compare(new SemVer(b));
24+
} catch (err) {
25+
log.error(`Failed to compare versions ${a} and ${b}`, err);
26+
// This is bad if it happens, but it's better than crashing, the tea semver library is very permissive
27+
// and it would be extremely unlikely for this to happen in practice as how would something get bottled in the first place?
28+
return a.localeCompare(b);
29+
}
30+
};
2231

2332
// Add a new version to the list of installed versions while maintaining the sort order
2433
export const addInstalledVersion = (
@@ -29,9 +38,7 @@ export const addInstalledVersion = (
2938
return [newVersion];
3039
}
3140

32-
return [...installedVersions, newVersion].sort((a, b) =>
33-
semverCompare(cleanVersion(b), cleanVersion(a))
34-
);
41+
return [...installedVersions, newVersion].sort((a, b) => semverCompare(b, a));
3542
};
3643

3744
export const findRecentInstalledVersion = (pkg: GUIPackage) => {
@@ -50,3 +57,13 @@ export const isInstalling = (pkg: GUIPackage) => {
5057
export const fixPackageName = (title: string) => {
5158
return title.replace("-", "\u2011");
5259
};
60+
61+
// Checks if an installed package is up to date. It is assumed that the package is installed.
62+
export const isPackageUpToDate = (pkg: GUIPackage) => {
63+
if (!pkg.installed_versions?.length) {
64+
return false;
65+
}
66+
67+
// if the installed version is equal or newer than the latest version, it's up to date
68+
return semverCompare(pkg.installed_versions[0], pkg.version) >= 0;
69+
};

modules/desktop/src/libs/stores/pkgs.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { notificationStore } from "$libs/stores";
2727
import withRetry from "$libs/utils/retry";
2828

2929
import log from "$libs/logger";
30+
import { isPackageUpToDate } from "../packages/pkg-utils";
3031

3132
const packageRefreshInterval = 1000 * 60 * 60; // 1 hour
3233

@@ -80,7 +81,7 @@ export default function initPackagesStore() {
8081
return PackageStates.AVAILABLE;
8182
}
8283

83-
const isUpToDate = pkg.version === pkg.installed_versions?.[0];
84+
const isUpToDate = isPackageUpToDate(pkg);
8485

8586
if (isInstalling(pkg)) {
8687
const hasNoVersions = !pkg.installed_versions?.length;

0 commit comments

Comments
 (0)