Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ permissions:

jobs:
test:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest]
python-version: ["3.11", "3.12", "3.13"]

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

### Added

- macOS 支持:按平台解析 DevEco JBR / hap-sign-tool / hdc 路径,可用 `hapsign` 命令行签名安装。
- 可安装的 `hapsign` 命令和标准 Python 项目元数据。
- Ruff、pytest、覆盖率、pre-commit 和 Windows CI 配置。
- 贡献指南、安全策略、行为准则和统一编辑器配置。
Expand Down
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ Profile、设备 UDID 或签名后的 HAP 加入仓库。

## 开发环境

项目要求 Windows Python 3.11 或更高版本。创建并激活虚拟环境后安装开发依赖:
项目要求 Windows 或 macOS,以及 Python 3.11 或更高版本。创建并激活虚拟环境后安装开发依赖:

```powershell
```bash
python -m pip install -r requirements-dev.txt
```

需要手动验证完整登录流程时,再安装 Playwright 浏览器:

```powershell
```bash
playwright install chromium
```

## 提交前检查

```powershell
```bash
python -m ruff format .
python -m ruff check .
python -m pytest --cov
Expand Down
40 changes: 25 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ Playwright 打开华为登录页(用户手动登录)

## 前置条件

1. **DevEco Studio** 已安装(需要其中的 JBR Java、hap-sign-tool.jar、hdc.exe
1. **DevEco Studio** 已安装(需要其中的 JBR Java、hap-sign-tool.jar、hdc)
2. **Python 3.11+**(推荐使用 conda 或 venv 隔离环境)
3. **HarmonyOS 设备**已通过 USB 连接并开启 USB 调试模式
4. **华为开发者账号**(需要已完成实名认证)

支持 **Windows** 与 **macOS**(命令行)。

## 安装

```bash
Expand All @@ -39,23 +41,31 @@ playwright install chromium

### 配置 DevEco Studio 路径

默认查找路径为 `D:\Program Files\Huawei\DevEco Studio`。如果你的 DevEco Studio 安装在其他位置,设置环境变量
默认查找路径

```bat
:: Windows CMD
- Windows: `D:\Program Files\Huawei\DevEco Studio`
- macOS: `/Applications/DevEco-Studio.app/Contents`

若安装在其他位置,设置环境变量:

```bash
# macOS / Linux shell
export DEVECO_HOME="/Applications/DevEco-Studio.app/Contents"

# Windows CMD
set DEVECO_HOME=E:\DevEco Studio

:: PowerShell
# Windows PowerShell
$env:DEVECO_HOME = "E:\DevEco Studio"
```

调试密钥库默认使用兼容 DevEco 调试流程的固定密码。如需覆盖,请设置:

```powershell
$env:HAPSIGN_KEYSTORE_PASSWORD = "使用你自己的强密码"
```bash
export HAPSIGN_KEYSTORE_PASSWORD="使用你自己的强密码"
```

### 配置 Python 路径(bat 脚本用)
### 配置 Python 路径(Windows bat 脚本用)

`sign_install.bat` 默认使用系统 PATH 中的 `python`。如果使用 conda/venv,设置环境变量:

Expand All @@ -69,19 +79,19 @@ $env:HAPSIGN_PYTHON = "C:\path\to\your\python.exe"

## 使用

### 方式一:拖拽(推荐)

将 `.hap` 文件直接拖到 `sign_install.bat` 上,自动完成签名+安装。

### 方式二:命令行
### 方式一:命令行(Windows / macOS)

```bash
hapsign --hap path\to\app-unsigned.hap
hapsign --hap path/to/app-unsigned.hap
```

包名会自动从 hap 内的 `module.json` 提取,无需手动指定。
源码目录中仍可使用 `python main.py --hap ...`。

### 方式二:拖拽(仅 Windows)

将 `.hap` 文件直接拖到 `sign_install.bat` 上,自动完成签名+安装。

### 完整参数

```
Expand Down Expand Up @@ -152,7 +162,7 @@ signing_files/com.example.myapp/
## 限制

- 登录验证码 / 二次验证需要用户在浏览器中手动处理
- 仅支持 Windows(SDK 路径、bat 脚本均为 Windows 环境)
- 拖拽安装脚本仅支持 Windows(`sign_install.bat`);macOS 请使用 `hapsign` 命令行
- 签名流程依赖华为云 API,需要有网络连接和华为开发者账号

## 开发与贡献
Expand Down
69 changes: 47 additions & 22 deletions hapsign/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
所有逆向获得的真实值集中在此,便于维护。"""

import os
import sys

# ── 域名 ──────────────────────────────────────────────────────
# 登录/认证域名(从 IdeSystem.properties 逆向获得)
Expand Down Expand Up @@ -73,29 +74,53 @@
ERR_APP_ID_INVALID = 205389959
ERR_TOKEN_INVALID_CODE = 4000 # 响应 code=4000 表示 token 失效


# ── 本机 SDK 路径(可通过环境变量覆盖)──────────────────────
_DEVECO_HOME = os.environ.get(
"DEVECO_HOME",
r"D:\Program Files\Huawei\DevEco Studio",
)
DEVECO_JBR = os.path.join(_DEVECO_HOME, "jbr", "bin", "java.exe")
HAP_SIGN_TOOL = os.path.join(
_DEVECO_HOME,
"sdk",
"default",
"openharmony",
"toolchains",
"lib",
"hap-sign-tool.jar",
)
HDC_PATH = os.path.join(
_DEVECO_HOME,
"sdk",
"default",
"openharmony",
"toolchains",
"hdc.exe",
)
def default_deveco_home(platform: str | None = None) -> str:
"""返回当前平台的 DevEco Studio 默认安装根目录。"""
plat = sys.platform if platform is None else platform
if plat == "darwin":
return "/Applications/DevEco-Studio.app/Contents"
if plat == "win32":
return r"D:\Program Files\Huawei\DevEco Studio"
return "/opt/DevEco-Studio"


def resolve_sdk_paths(
deveco_home: str | None = None,
platform: str | None = None,
) -> tuple[str, str, str, str]:
"""解析 java / hap-sign-tool / hdc / keytool 路径。

Returns:
(java, hap_sign_tool, hdc, keytool)
"""
plat = sys.platform if platform is None else platform
home = deveco_home if deveco_home is not None else default_deveco_home(plat)
toolchains = os.path.join(home, "sdk", "default", "openharmony", "toolchains")
hap_sign_tool = os.path.join(toolchains, "lib", "hap-sign-tool.jar")

if plat == "darwin":
bin_dir = os.path.join(home, "jbr", "Contents", "Home", "bin")
java = os.path.join(bin_dir, "java")
keytool = os.path.join(bin_dir, "keytool")
hdc = os.path.join(toolchains, "hdc")
elif plat == "win32":
bin_dir = os.path.join(home, "jbr", "bin")
java = os.path.join(bin_dir, "java.exe")
keytool = os.path.join(bin_dir, "keytool.exe")
hdc = os.path.join(toolchains, "hdc.exe")
else:
bin_dir = os.path.join(home, "jbr", "bin")
java = os.path.join(bin_dir, "java")
keytool = os.path.join(bin_dir, "keytool")
hdc = os.path.join(toolchains, "hdc")

return java, hap_sign_tool, hdc, keytool


_DEVECO_HOME = os.environ.get("DEVECO_HOME") or default_deveco_home()
DEVECO_JBR, HAP_SIGN_TOOL, HDC_PATH, KEYTOOL_PATH = resolve_sdk_paths(_DEVECO_HOME)

# ── HTTP header 常量 ──────────────────────────────────────────
HEADER_OAUTH2_TOKEN = "oauth2Token"
Expand Down
5 changes: 2 additions & 3 deletions hapsign/signing/keytool_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ class KeytoolUtil:

@staticmethod
def _get_keytool_path() -> str:
"""从 DEVECO_JBR 路径推导 keytool.exe 路径。"""
jbr_dir = os.path.dirname(config.DEVECO_JBR)
return os.path.join(jbr_dir, "keytool.exe")
"""返回与当前平台匹配的 keytool 路径。"""
return config.KEYTOOL_PATH

def generate_keypair(
self,
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ classifiers = [
"Development Status :: 3 - Alpha",
"Environment :: Console",
"License :: OSI Approved :: MIT License",
"Operating System :: MacOS",
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
Expand Down
34 changes: 34 additions & 0 deletions tests/test_config_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""SDK 路径解析测试。"""

import os

from hapsign.config import default_deveco_home, resolve_sdk_paths


def test_default_deveco_home_by_platform() -> None:
assert default_deveco_home("darwin") == "/Applications/DevEco-Studio.app/Contents"
assert default_deveco_home("win32") == r"D:\Program Files\Huawei\DevEco Studio"
assert default_deveco_home("linux") == "/opt/DevEco-Studio"


def test_resolve_sdk_paths_darwin() -> None:
home = "/Applications/DevEco-Studio.app/Contents"
java, hap_sign, hdc, keytool = resolve_sdk_paths(home, "darwin")

assert java == os.path.join(home, "jbr", "Contents", "Home", "bin", "java")
assert keytool == os.path.join(home, "jbr", "Contents", "Home", "bin", "keytool")
assert hdc.endswith(os.path.join("toolchains", "hdc"))
assert hap_sign.endswith(os.path.join("lib", "hap-sign-tool.jar"))
assert not java.endswith(".exe")
assert not hdc.endswith(".exe")


def test_resolve_sdk_paths_win32() -> None:
home = r"D:\Program Files\Huawei\DevEco Studio"
java, hap_sign, hdc, keytool = resolve_sdk_paths(home, "win32")

assert java.endswith("java.exe")
assert keytool.endswith("keytool.exe")
assert hdc.endswith("hdc.exe")
assert hap_sign.endswith("hap-sign-tool.jar")
assert os.path.join("jbr", "bin") in java.replace("/", "\\") or "jbr" in java