Skip to content

feat: HarmonyOS support (auth, AGC signing, build, template) - #17

Merged
frankplus merged 3 commits into
mainfrom
feat/harmonyos-support
Sep 17, 2026
Merged

frankplus merged 3 commits into
mainfrom
feat/harmonyos-support

Conversation

@frankplus

Copy link
Copy Markdown
Contributor

Summary

Adds HarmonyOS support next to OpenHarmony. A project is HarmonyOS only when a product in build-profile.json5 declares runtimeOS: "HarmonyOS", which DevEco Studio writes. Every other project works exactly as before, and OpenHarmony signing still runs offline.

  • oniro-app auth login|logout|status|team list: signs in to a Huawei developer account in the browser. The token is stored encrypted under ONIRO_HARMONYOS_AUTH_DIR. Accounts from any Huawei region work; Huawei's own CLI accepts only CN accounts.
  • oniro-app sign --harmonyos [--team-id] [--product] [--force]: AppGallery Connect issues a debug certificate and profile, the material is checked, and an encrypted signingConfig is written into build-profile.json5. Material that is still valid gets reused. The certificate is named oniro_debug_<team>.cer, so DevEco Studio's certificate is never revoked.
  • oniro-app build builds HarmonyOS projects against the HarmonyOS SDK (DEVECO_SDK_HOME) with that install's own hvigor and ohpm. The SDK is found in DevEco Studio's default location, in the HarmonyOS command-line tools, or at ONIRO_HARMONYOS_SDK_PATH.
  • oniro-app create --template HarmonyOSApp creates a HarmonyOS project.

Parts of packages/core/src/harmonyos/ are adapted from the MIT-licensed openharmony-sig/deveco-cli. packages/core/src/harmonyos/NOTICE.md lists each adapted file, what changed and the license text.

Review pass (last commit)

  • Node 20: undici@8 needs Node ≥ 22.19, but the packages declare Node ≥ 20 and CI runs on Node 20. It now uses undici@7 with its own fetch.
  • Login race: a browser callback that came back before waitForCallback was called got dropped, and login timed out after 10 minutes. This happens whenever the browser launcher exits only when the browser closes (xdg-open without a desktop environment, or $BROWSER). The callback server now keeps the callback from the start, and login no longer waits for the launcher.
  • Reuse without calling AGC: connected devices are now read over hdc before the reuse check, so AppGallery Connect is only contacted when the material is regenerated.
  • Simpler code: undici's EnvHttpProxyAgent replaces the hand-written HTTP(S)_PROXY / NO_PROXY handling. The token store no longer wraps a second key, since both keys sat on the same disk.

Test plan

  • npm run typecheck, npm run build, npm test: 431 core tests pass (17 skipped) and 58 CLI tests pass
  • New: harmonyOsAutoSign end to end against an in-memory AGC with the real fixture keystore. Covers issuing, reuse without AGC calls, forced and implied regeneration, replacing only our own certificate, team and product overrides, and the no-device and verification failures
  • New: hap-sign-tool invocation through a stand-in java; the HTTP client against a loopback server; buildHap/runHvigorw for HarmonyOS and OpenHarmony projects with stand-in tools; the HarmonyOS fallback in getHdcPath; AGC device paging; session login, logout and stored state; callback-server edge cases
  • New CLI smoke tests: auth status/logout/team list and sign --harmonyos while signed out
  • Manual, from the earlier commits: full flow with an EU account and no device attached. AGC issued a certificate and a profile naming the team's 6 devices, hap-sign-tool verify-profile accepted them, and a rerun reused the material. OpenHarmony create/sign/build output matches main
  • CI on Linux, macOS and Windows

🤖 Generated with Claude Code

Francesco Pham added 3 commits September 17, 2026 10:45
Adds a `harmonyos/` subtree to @oniroproject/core so the toolchain can build
and sign HarmonyOS apps as well as OpenHarmony ones. A project is HarmonyOS
only when a product in build-profile.json5 declares `runtimeOS: "HarmonyOS"`,
as DevEco Studio writes. Every other project is OpenHarmony and takes the same
code path as before, offline signing included.

Project targeting
  `detectRuntimeOs` reads `runtimeOS` from the named product, else the first
  one. A missing or unreadable profile means OpenHarmony.

SDK resolution
  The HarmonyOS SDK ships only inside DevEco Studio or the login-gated
  HarmonyOS command-line tools, so it is located rather than downloaded:
  `harmonyosSdkPath`, then DevEco Studio's default install location, then
  `cmdToolsPath` when those tools are the HarmonyOS edition. Both SDK flavours
  share one layout, so an autodetected candidate must also name itself
  HarmonyOS in sdk-pkg.json; otherwise the OpenHarmony command-line tools
  would be accepted and then fail deep inside hvigor.

Account sign-in
  `createHarmonyOsSession` implements the browser sign-in: a loopback callback
  server with a constant-time secret check, the temp-token exchange, and the
  session token stored envelope-encrypted under `harmonyosAuthDir`.

  Accounts from every Huawei region work. Upstream hardcodes the
  Chinese-mainland hosts and refuses other accounts. Here sign-in starts on the
  CN page, which redirects to the account's own region; the callback's siteId
  then selects that region's login and AGC hosts. The host table and the `site`
  parameter (DE for siteId 7) are what DevEco Studio gets from Huawei's Global
  Routing Service.

  Real-name verification is not required. As in DevEco Studio, an account may
  sign once it has accepted the HUAWEI Developer Basic Service Agreement;
  `resolveAgcAuth` checks this and never accepts it on the user's behalf.

Signing
  `harmonyOsAutoSign` mirrors DevEco Studio's automatic signing: keystore and
  CSR via hap-sign-tool, a debug certificate from AGC, registration of any
  connected devices, a debug provisioning profile naming all of the team's
  registered devices, then an encrypted signingConfig merged into
  build-profile.json5. No device needs to be attached when the team already
  has some registered.

  Certificates and profiles come from a small per-team quota, so existing
  material is reused unless it no longer matches the project, team, devices or
  requested ACL permissions. `shouldRegenerate` is that decision as a pure
  function, tested directly.

  Downloaded material is verified before it is written: the certificate chain
  is in date, the profile was issued for the leaf certificate (by X.509
  fingerprint), and the keystore holds the certified key. node-forge parses
  only RSA certificates while hap-sign-tool generates ECC keys, so the
  keystore's certificate goes to Node's X.509 parser; tests use real
  hap-sign-tool keystores as fixtures.

  The AGC certificate is named oniro_debug_<team>.cer. Upstream shares DevEco
  Studio's auto_debug_<team>.cer, and regenerating replaces the certificate by
  name, so the two tools would revoke each other's material.

Build and scaffold wiring
  runHvigorw passes the HarmonyOS SDK as DEVECO_SDK_HOME (not
  OHOS_BASE_SDK_HOME) and uses the project's hvigorw or the HarmonyOS
  install's. buildHap installs dependencies with that install's ohpm and points
  the missing-signature warning at `sign --harmonyos`. getHdcPath falls back to
  the HarmonyOS SDK's hdc when the OpenHarmony command-line tools are absent.

  createScaffold writes the `"<version>(<api>)"` SDK label a HarmonyOS template
  expects. hvigor builds only against the release compileSdkVersion names, so
  the installed SDK's own release from sdk-pkg.json is preferred over the
  built-in API table, with a warning when they differ.

The new config keys get their ONIRO_HARMONYOS_* environment variables in the
CLI's config adapter here, since it maps every ConfigKey exhaustively.

The AGC endpoints are private and undocumented. Parts of this subtree are
adapted from the MIT-licensed openharmony-sig/deveco-cli; each file names its
upstream origin, and packages/core/src/harmonyos/NOTICE.md carries the file
map, the deviations and the license text.

Signed-off-by: Francesco Pham <francesco.pham1@h-partners.com>
Surfaces the core HarmonyOS support through the CLI.

  oniro-app auth login|logout|status|team list
      Browser sign-in to a Huawei developer account, needed only for HarmonyOS
      signing. The login URL is printed as well as opened, so a headless host
      can be signed in from another machine. `status` warns when the account
      cannot sign yet, and `--json` reports the region and
      developerAgreement.

  oniro-app sign --harmonyos [--team-id] [--product] [--force]
      Routes to the AppGallery Connect flow instead of the offline one, and
      reports how many devices the issued profile names.

      Plain `sign` on a HarmonyOS project now fails with the fix rather than
      producing a HAP signed with the OpenHarmony development certificate.
      That builds fine and then will not install, which is a much worse place
      to find out.

  oniro-app create --template HarmonyOSApp
      Scaffolds an empty HarmonyOS ability. `--sdk` takes the API level of the
      installed HarmonyOS SDK.

`build` needs no new flags: it reads the runtime from build-profile.json5.

Verified with an EU account and no device attached: sign-in, session refresh
and team listing succeed; `sign --harmonyos` has AGC issue a certificate and
a debug profile naming the team's 6 devices, hap-sign-tool verify-profile
accepts them, DevEco Studio's own certificate is left intact, and a rerun
reuses the material without writing to AGC.

OpenHarmony behaviour is unchanged: create, sign, sign --bootstrap and build
for the EmptyAbility and NativeCpp templates give the same exit codes, output
(bar hap-sign-tool timestamps) and generated project files as on main, and
both build signed HAPs.

Signed-off-by: Francesco Pham <francesco.pham1@h-partners.com>
…iden tests

A review pass over the HarmonyOS support.

Fixes
  - undici 8 requires Node >= 22.19, but both packages declare Node >= 20
    and build for node20. Moved to undici 7 (Node >= 20.18.1), and its own
    fetch is used with its dispatcher, so the two never come from different
    undici versions.
  - A browser callback that arrived before `waitForCallback` was called was
    dropped, and login then timed out after 10 minutes. `login` awaited the
    browser launcher first, and xdg-open without a desktop environment, or
    $BROWSER, exits only when the browser does. The callback server now
    holds the callback from the start, and the launcher is detached and not
    awaited.
  - `sign --harmonyos` listed the team's devices in AGC before deciding
    whether the existing material could be reused. Connected devices are now
    read over hdc first, and AGC is called only when regenerating, as the
    README already says; still before anything is deleted.

Simplifications
  - Hand-rolled HTTP(S)_PROXY / NO_PROXY resolution and the per-proxy agent
    cache are replaced by undici's EnvHttpProxyAgent.
  - The token store drops its wrapped data key: the token is encrypted with
    the key kept outside the auth directory. Every key was on the same disk,
    so the second layer protected nothing the first did not.

Tests
  - harmonyOsAutoSign end to end against an in-memory AGC with the real
    fixture keystore: issuance, reuse without AGC calls, forced and implied
    regeneration, replacing only oniro-app's certificate, team and product
    overrides, and the no-device and verification failures.
  - hap-sign-tool invocation through a stand-in java, the HTTP client against
    a loopback server, buildHap/runHvigorw for HarmonyOS and OpenHarmony
    projects with stand-in tools, getHdcPath's HarmonyOS fallback, AGC device
    paging, session login/logout/state, and callback-server edge cases.
  - CLI: auth status/logout/team list and sign --harmonyos while signed out.
  - Token-store tests no longer write a key into the real home directory.

Signed-off-by: Francesco Pham <francesco.pham1@h-partners.com>
@frankplus
frankplus merged commit cbe7c58 into main Sep 17, 2026
10 checks passed
@frankplus
frankplus deleted the feat/harmonyos-support branch September 17, 2026 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant