fix(harmony): discover existing official HDC installations
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"id": "dev.trove.harmony-connector",
|
||||
"name": "Harmony Connector",
|
||||
"description": "连接鸿蒙设备,收发完整文件夹、管理应用,查看手机画面并通过键鼠操作。",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.2",
|
||||
"engine": { "trove": ">=0.1.0 <1.0.0" },
|
||||
"icon": "assets/icon.svg",
|
||||
"ui": {
|
||||
|
||||
+1
-1
@@ -204,7 +204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "harmony-connector"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "harmony-connector"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
||||
@@ -129,11 +129,65 @@ fn tool_version(path: &Path, cancel: &AtomicBool) -> Result<String> {
|
||||
}
|
||||
Ok(version.trim().to_owned())
|
||||
}
|
||||
fn installed_toolkits(home: &Path) -> Vec<PathBuf> {
|
||||
let root = home.join("Library/HarmonyConnector/tools");
|
||||
let mut versions: Vec<_> = fs::read_dir(&root)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flatten()
|
||||
.filter(|entry| entry.path().is_dir())
|
||||
.filter_map(|entry| {
|
||||
let version = entry
|
||||
.file_name()
|
||||
.to_str()?
|
||||
.split('.')
|
||||
.map(str::parse::<u64>)
|
||||
.collect::<std::result::Result<Vec<_>, _>>()
|
||||
.ok()?;
|
||||
Some((version, entry.path()))
|
||||
})
|
||||
.collect();
|
||||
versions.sort_by(|a, b| b.cmp(a));
|
||||
let mut candidates: Vec<_> = versions
|
||||
.into_iter()
|
||||
.filter_map(|(_, path)| selected_tool(&path))
|
||||
.collect();
|
||||
candidates.extend(selected_tool(&root));
|
||||
if !candidates.is_empty() {
|
||||
return candidates;
|
||||
}
|
||||
// Only inspect named, extracted toolkits directly under Downloads. ZIPs
|
||||
// and unfinished browser downloads are not executable installations.
|
||||
let mut downloads: Vec<_> = fs::read_dir(home.join("Downloads"))
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flatten()
|
||||
.filter(|entry| {
|
||||
let name = entry.file_name().to_string_lossy().to_ascii_lowercase();
|
||||
entry.path().is_dir()
|
||||
&& (name.starts_with("command-line-tools") || name.starts_with("commandline-tools"))
|
||||
})
|
||||
.map(|entry| entry.path())
|
||||
.collect();
|
||||
downloads.sort();
|
||||
candidates.extend(
|
||||
downloads
|
||||
.into_iter()
|
||||
.filter_map(|path| selected_tool(&path)),
|
||||
);
|
||||
candidates
|
||||
}
|
||||
fn discover() -> Option<PathBuf> {
|
||||
let mut candidates = Vec::new();
|
||||
if let Some(path) = env::var_os("PATH") {
|
||||
candidates.extend(env::split_paths(&path).map(|p| p.join("hdc")));
|
||||
}
|
||||
if let Some(path) = candidates.iter().find(|p| executable(p)) {
|
||||
return path.canonicalize().ok();
|
||||
}
|
||||
if let Some(home) = env::var_os("HOME") {
|
||||
candidates.extend(installed_toolkits(Path::new(&home)));
|
||||
}
|
||||
for root in [
|
||||
Some(PathBuf::from("/Applications")),
|
||||
env::var_os("HOME").map(|p| PathBuf::from(p).join("Applications")),
|
||||
|
||||
@@ -74,6 +74,63 @@ fn rechecks_hdc_installed_after_manager_start() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn automatically_discovers_downloaded_toolkit_without_manual_configuration() {
|
||||
const CHILD: &str = "HARMONY_DOWNLOADED_TOOL_TEST";
|
||||
if let Some(root) = env::var_os(CHILD) {
|
||||
let root = PathBuf::from(root);
|
||||
let installations = root.join("Library/HarmonyConnector/tools");
|
||||
let previous = installations.join("9.9.0.1/toolchains/hdc");
|
||||
let current = installations.join("26.0.0.821/toolchains/hdc");
|
||||
version_tool(&previous);
|
||||
version_tool(¤t);
|
||||
// An incomplete, newer extraction must not hide a usable installation.
|
||||
fs::create_dir_all(installations.join("27.0.0.1/toolchains")).unwrap();
|
||||
let manager = Manager::new();
|
||||
let result = manager.call("tool", json!({})).unwrap();
|
||||
assert_eq!(result["available"], true, "{result}");
|
||||
assert_eq!(result["path"], json!(current.canonicalize().unwrap()));
|
||||
assert!(!root.join("data/harmony-tools.json").exists());
|
||||
// A user's explicit selection still wins over automatic discovery.
|
||||
let chosen = root.join("chosen/hdc");
|
||||
version_tool(&chosen);
|
||||
manager.call("configure", json!({"path": chosen})).unwrap();
|
||||
assert_eq!(
|
||||
Manager::new().call("tool", json!({})).unwrap()["path"],
|
||||
json!(chosen.canonicalize().unwrap())
|
||||
);
|
||||
fs::remove_file(chosen).unwrap();
|
||||
fs::remove_dir_all(installations).unwrap();
|
||||
// Also support a normal Finder extraction in Downloads, retaining the
|
||||
// official command-line-tools/sdk/default/openharmony layout.
|
||||
let downloaded = root.join("Downloads/commandline-tools-mac-arm64-26.0.0.821/command-line-tools/sdk/default/openharmony/toolchains/hdc");
|
||||
version_tool(&downloaded);
|
||||
let result = Manager::new().call("tool", json!({})).unwrap();
|
||||
assert_eq!(result["available"], true);
|
||||
assert_eq!(result["path"], json!(downloaded.canonicalize().unwrap()));
|
||||
return;
|
||||
}
|
||||
let root = tempfile::tempdir().unwrap();
|
||||
let output = std::process::Command::new(env::current_exe().unwrap())
|
||||
.args([
|
||||
"--exact",
|
||||
"hdc::tests::automatically_discovers_downloaded_toolkit_without_manual_configuration",
|
||||
"--nocapture",
|
||||
])
|
||||
.env(CHILD, root.path())
|
||||
.env("HOME", root.path())
|
||||
.env("PATH", root.path().join("bin"))
|
||||
.env("TROVE_DATA_DIR", root.path().join("data"))
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"{}\n{}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
fn fixture(debug: bool) -> (tempfile::TempDir, Arc<Manager>) {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let path = directory.path().join("hdc");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Harmony Connector
|
||||
|
||||
插件 ID:`dev.trove.harmony-connector`。当前版本:`0.3.1`。
|
||||
插件 ID:`dev.trove.harmony-connector`。当前版本:`0.3.2`。
|
||||
|
||||
macOS 上的鸿蒙设备连接工具。通过 USB 收发文件与完整文件夹;使用官方 HDC 管理应用、保存截图、导出日志和调试应用数据。功能按连接工具、设备实际能力和授权开放,不按 HarmonyOS 大版本或机型名称限制。
|
||||
|
||||
@@ -39,7 +39,7 @@ macOS 上的鸿蒙设备连接工具。通过 USB 收发文件与完整文件夹
|
||||
2. 完整解压 ZIP,点击「选择已解压的工具目录」。可选择包含 `command-line-tools` 的解压目录、`command-line-tools` 本身,或其 `toolchains` 目录;插件定位 HDC、运行版本检查,成功后保存路径。错误选择和取消不会覆盖已有配置。
|
||||
3. 手机连接 USB,启用开发者模式与 USB 调试,解锁并允许这台 Mac 的调试请求。配置成功后自动检查设备;「刷新连接」也会重新检查工具,支持启动后才安装到 PATH 的 HDC。
|
||||
|
||||
已有 DevEco Studio 或 SDK 的用户也可点击「选择 HDC 文件」。插件启动时尝试发现 PATH 或 `/Applications`、`~/Applications` 中 DevEco Studio 的内置工具。典型路径:
|
||||
已有 DevEco Studio 或 SDK 的用户也可点击「选择 HDC 文件」。插件保留手动选择的路径;未配置时,按 PATH、`~/Library/HarmonyConnector/tools` 中的工具安装、`~/Downloads` 中已解压的 Command Line Tools、`/Applications` 和 `~/Applications` 中的 DevEco Studio 依次查找。工具安装目录按数字版本从新到旧检查,跳过未包含可执行 HDC 的目录;Downloads 只检查名称以 `command-line-tools` 或 `commandline-tools` 开头的直接子目录及 SDK 的标准位置,不递归扫描用户文件、不将 ZIP 视为已安装工具。典型路径:
|
||||
|
||||
```text
|
||||
/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc
|
||||
@@ -111,6 +111,8 @@ curios/harmony-connector/backend/harmony-connector --check-screen /absolute/path
|
||||
|
||||
0.3.1 的安装引导验证通过 41 项原生测试、30 项 UI/格式/坐标测试和 6 项 USB/协议检查(共 77 项)。新增回归覆盖官网下载入口、选择已解压目录、取消/错误选择保留配置、启动后安装工具、工具丢失后返回引导。浏览器以明确标记的模拟工具检查了引导和配置后的界面切换。使用已下载的官方工具目录调用实际发布后端,成功定位 HDC 并返回 `Ver: 3.2.0f`;设备当时仍为 Unauthorized,后续投屏验收因无已授权设备而停止。
|
||||
|
||||
0.3.2 补上工具安装目录和 Downloads 的自动发现。回归在独立 HOME、空 PATH 工具目录、未保存任何设置的进程中运行真实 `Manager::new → hdc.tool`,验证数字版本选择、未完成解压跳过、手动路径优先和官方解压目录布局。实际 Mac 验证通过 Wire 请求 `hdc.tool`,使用空插件数据目录和系统 PATH,未调用 `configure`;0.3.1 返回 `available: false`,0.3.2 自动找到之前解压的工具并返回 `Ver: 3.2.0f`。USB 目标仍为 Unauthorized。该版 42 项原生测试、30 项 UI/格式/坐标测试及 6 项 USB/协议检查全部通过。
|
||||
|
||||
## 构建与发布
|
||||
|
||||
要求 macOS、Xcode Command Line Tools、Rust/Cargo、Node.js、pnpm、make、pkg-config。固定源码归档与哈希位于 `vendor/harmony-connector/`。
|
||||
|
||||
+16
-1
@@ -2,7 +2,7 @@
|
||||
"schemaVersion": 1,
|
||||
"id": "trove-official",
|
||||
"name": "Trove 官方软件源",
|
||||
"generatedAt": "2026-09-10T06:33:16.439Z",
|
||||
"generatedAt": "2026-09-10T06:45:52.251Z",
|
||||
"curios": [
|
||||
{
|
||||
"id": "dev.trove.qa.host",
|
||||
@@ -200,6 +200,21 @@
|
||||
"name": "Harmony Connector",
|
||||
"description": "连接鸿蒙设备,收发完整文件夹、管理应用,查看手机画面并通过键鼠操作。",
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.3.2",
|
||||
"engine": {
|
||||
"trove": ">=0.1.0 <1.0.0"
|
||||
},
|
||||
"releasedAt": "2026-09-10T06:45:52.251Z",
|
||||
"artifacts": [
|
||||
{
|
||||
"target": "aarch64-apple-darwin",
|
||||
"url": "packages/dev.trove.harmony-connector/0.3.2/aarch64-apple-darwin.curio",
|
||||
"sha256": "eb0ec5d5a8b7f4e3150cd80351eaadd5ef13535d8cbf388e955218b0f60b0f3d",
|
||||
"size": 2939665
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.3.1",
|
||||
"engine": {
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user