Skip to content
This repository was archived by the owner on Apr 7, 2026. It is now read-only.

Commit 3536e99

Browse files
authored
feat: Add simple globe runtime install (#162)
* add simple runtime install * fix issues
1 parent 43f6570 commit 3536e99

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

packages/globe_cli/lib/src/command_runner.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'commands/build_logs_command.dart';
99
import 'commands/commands.dart';
1010
import 'commands/create_project_command.dart';
1111
import 'commands/project_command.dart';
12+
import 'commands/runtime_command.dart';
1213
import 'commands/update.dart';
1314
import 'get_it.dart';
1415
import 'graphql/client.dart';
@@ -74,6 +75,7 @@ class GlobeCliCommandRunner extends CompletionCommandRunner<int> {
7475
addCommand(BuildLogsCommand());
7576
addCommand(TokenCommand());
7677
addCommand(ProjectCommand());
78+
addCommand(RuntimeCommand());
7779
addCommand(CreateProjectFromTemplate());
7880
addCommand(WhoamiCommand());
7981
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import 'dart:async';
2+
import 'dart:convert';
3+
import 'dart:ffi';
4+
import 'dart:io';
5+
6+
import 'package:collection/collection.dart';
7+
import 'package:http/http.dart' as http;
8+
import 'package:mason_logger/mason_logger.dart';
9+
import 'package:path/path.dart' as path;
10+
11+
import '../../command.dart';
12+
import '../../utils/api.dart';
13+
14+
String get dylibName {
15+
final currentAbi = Abi.current();
16+
17+
return switch (currentAbi) {
18+
Abi.macosX64 => 'libglobe_runtime-x86_64-apple-darwin.dylib',
19+
Abi.macosArm64 => 'libglobe_runtime-aarch64-apple-darwin.dylib',
20+
Abi.linuxX64 => 'libglobe_runtime-x86_64-unknown-linux-gnu.so',
21+
Abi.linuxArm64 => 'libglobe_runtime-aarch64-unknown-linux-gnu.so',
22+
Abi.windowsX64 => 'globe_runtime-x86_64-pc-windows-msvc.dll',
23+
_ => throw UnsupportedError('Unsupported ABI: $currentAbi'),
24+
};
25+
}
26+
27+
String get globeRuntimeInstallDirectory {
28+
return path.join(userHomeDirectory, '.globe', 'runtime');
29+
}
30+
31+
String get userHomeDirectory {
32+
if (Platform.isWindows) {
33+
return Platform.environment['USERPROFILE'] ?? r'C:\Users\Default';
34+
} else {
35+
return Platform.environment['HOME'] ?? '/';
36+
}
37+
}
38+
39+
final class RuntimeVersion {
40+
final String name;
41+
final String version;
42+
final String releaseUrl;
43+
final String binaryUrl;
44+
45+
const RuntimeVersion({
46+
required this.name,
47+
required this.version,
48+
required this.binaryUrl,
49+
required this.releaseUrl,
50+
});
51+
52+
factory RuntimeVersion.fromJson(Map<String, dynamic> json) {
53+
final assets = json['assets'] as Iterable;
54+
if (assets.isEmpty) throw Exception('No assets found in release');
55+
56+
final asset = assets.firstWhereOrNull(
57+
(asset) => (asset as Map<dynamic, dynamic>)['name'] == dylibName,
58+
) as Map<dynamic, dynamic>;
59+
60+
return RuntimeVersion(
61+
version: json['tag_name'] as String,
62+
releaseUrl: json['html_url'] as String,
63+
binaryUrl: asset['browser_download_url'] as String,
64+
name: asset['name'] as String,
65+
);
66+
}
67+
68+
Future<void> download() async {
69+
final url = Uri.parse(binaryUrl);
70+
final response = await http.readBytes(url);
71+
72+
final directory = Directory(globeRuntimeInstallDirectory);
73+
await directory.create(recursive: true);
74+
75+
final nameParts = name.split('.');
76+
final versionedName = path.join(
77+
directory.path,
78+
'${nameParts.first}_$version.${nameParts.last}',
79+
);
80+
81+
final file = File(versionedName);
82+
await file.writeAsBytes(response);
83+
}
84+
}
85+
86+
Future<RuntimeVersion> getLatestVersion() async {
87+
final url = Uri.parse(
88+
'https://api.github.com/repos/invertase/globe_runtime/releases/latest',
89+
);
90+
final response = await http.get(url);
91+
if (response.statusCode != 200) {
92+
throw Exception('Failed to fetch release info');
93+
}
94+
return RuntimeVersion.fromJson(
95+
jsonDecode(response.body) as Map<String, dynamic>,
96+
);
97+
}
98+
99+
class RuntimeInstallCommand extends BaseGlobeCommand {
100+
RuntimeInstallCommand();
101+
102+
@override
103+
String get description => 'Pause the current globe project';
104+
105+
@override
106+
String get name => 'install';
107+
108+
@override
109+
FutureOr<int> run() async {
110+
final runtimeDownloadProgress =
111+
logger.progress('Fetching Globe Runtime latest version');
112+
113+
try {
114+
final release = await getLatestVersion();
115+
116+
runtimeDownloadProgress.update(
117+
'Installing ${cyan.wrap('Globe Runtime')} @ ${cyan.wrap(release.version)}',
118+
);
119+
120+
await release.download();
121+
122+
runtimeDownloadProgress.complete('Globe Runtime.installed.');
123+
return ExitCode.success.code;
124+
} on ApiException catch (e) {
125+
runtimeDownloadProgress.fail('✗ Failed to pause project: ${e.message}');
126+
return ExitCode.software.code;
127+
} catch (e, s) {
128+
runtimeDownloadProgress.fail('✗ Failed to pause project: $e');
129+
logger.detail(s.toString());
130+
return ExitCode.software.code;
131+
}
132+
}
133+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import '../command.dart';
2+
import 'runtime/runtime_install_command.dart';
3+
4+
class RuntimeCommand extends BaseGlobeCommand {
5+
RuntimeCommand() {
6+
addSubcommand(RuntimeInstallCommand());
7+
}
8+
9+
@override
10+
String get description => 'Manage globe runtime.';
11+
12+
@override
13+
String get name => 'runtime';
14+
}

0 commit comments

Comments
 (0)