From bffb385b69353b39b5d6edf2b052e2a68b79ee29 Mon Sep 17 00:00:00 2001 From: ZHAO Xudong Date: Wed, 15 Jul 2026 17:54:32 +0800 Subject: [PATCH] build: switch from HAP to APP package output --- .github/workflows/build.yml | 30 ++++++++-------- docs/ARCHITECTURE.md | 10 +++--- docs/BUILD.md | 44 +++++++++++++---------- scripts/build-app.sh | 71 +++++++++++++++++++++---------------- 4 files changed, 86 insertions(+), 69 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e20fc1e..0577505 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build HarmonyOS HAP +name: Build HarmonyOS APP on: push: @@ -175,7 +175,7 @@ jobs: fi cat AppScope/app.json5 - # ── Step 6: Build & sign the HAP ───────────────────────────────────── + # ── Step 6: Build & sign the APP ───────────────────────────────────── - name: Build HarmonyOS app run: ./scripts/build-app.sh --${{ github.event.inputs.build_mode || 'release' }} env: @@ -185,23 +185,23 @@ jobs: KEY_PASSWORD: ${{ secrets.OHOS_KEY_PASSWORD }} KEY_ALIAS: ${{ secrets.OHOS_KEY_ALIAS }} # ── Step 7: Upload artifact ────────────────────────────────────────── - - name: Find HAP file - id: find_hap + - name: Find APP file + id: find_app run: | - HAP_FILE=$(find entry/build -name "*.hap" -type f | head -1) - if [ -z "${HAP_FILE}" ]; then - echo "::error::No .hap file found!" + APP_FILE=$(find build/outputs -name "*.app" -type f | head -1) + if [ -z "${APP_FILE}" ]; then + echo "::error::No .app file found!" exit 1 fi - echo "hap_path=${HAP_FILE}" >> $GITHUB_OUTPUT - echo "hap_name=$(basename ${HAP_FILE})" >> $GITHUB_OUTPUT - echo "Found HAP: ${HAP_FILE} ($(du -h ${HAP_FILE} | cut -f1))" + echo "app_path=${APP_FILE}" >> $GITHUB_OUTPUT + echo "app_name=$(basename ${APP_FILE})" >> $GITHUB_OUTPUT + echo "Found APP: ${APP_FILE} ($(du -h ${APP_FILE} | cut -f1))" - - name: Upload HAP artifact + - name: Upload APP artifact uses: actions/upload-artifact@v4 with: name: electerm-harmony-${{ github.sha }} - path: ${{ steps.find_hap.outputs.hap_path }} + path: ${{ steps.find_app.outputs.app_path }} retention-days: 30 # ── Summary ────────────────────────────────────────────────────────── @@ -217,7 +217,7 @@ jobs: echo "| ohos-node | \`${{ env.OHOS_NODE_VERSION }}\` |" >> $GITHUB_STEP_SUMMARY echo "| electerm-web | \`${{ env.ELECTERM_WEB_REPO }}@${{ env.ELECTERM_WEB_REF }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Build mode | \`${{ github.event.inputs.build_mode || 'release' }}\` |" >> $GITHUB_STEP_SUMMARY - if [ -f "${{ steps.find_hap.outputs.hap_path }}" ]; then - echo "| HAP file | \`${{ steps.find_hap.outputs.hap_name }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| HAP size | \`$(du -h ${{ steps.find_hap.outputs.hap_path }} | cut -f1)\` |" >> $GITHUB_STEP_SUMMARY + if [ -f "${{ steps.find_app.outputs.app_path }}" ]; then + echo "| APP file | \`${{ steps.find_app.outputs.app_name }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| APP size | \`$(du -h ${{ steps.find_app.outputs.app_path }} | cut -f1)\` |" >> $GITHUB_STEP_SUMMARY fi diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 208a643..ed8072d 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -114,20 +114,20 @@ GitHub Actions (ubuntu-latest, x64) │ ├── OHOS_CERT_B64 → signing/electerm_publish.cer │ └── OHOS_PROFILE_B64 → signing/electermRelease.p7b ├── 9. Configure bundle name (from OHOS_BUNDLE_NAME secret → app.json5) -├── 10. Build unsigned HAP (two-phase signing, phase 1) +├── 10. Build unsigned APP (two-phase signing, phase 1) │ ├── Generate build-profile.json5 (empty signingConfigs) │ ├── ohpm install -│ └── hvigorw assembleHap -p enableSignTask=false -├── 11. Sign HAP with hap-sign-tool.jar (two-phase signing, phase 2) +│ └── hvigorw assembleApp -p enableSignTask=false +├── 11. Sign APP with hap-sign-tool.jar (two-phase signing, phase 2) │ └── java -jar hap-sign-tool.jar sign-app -mode localSign ... -└── 12. Upload .hap artifact (retained 30 days) +└── 12. Upload .app artifact (retained 30 days) ``` ### Two-Phase Signing This project does **not** use hvigor's built-in signer (which requires DevEco Studio's encrypted passwords). Instead: -1. **Build unsigned** — `hvigorw assembleHap` with `-p enableSignTask=false` and empty `signingConfigs` +1. **Build unsigned** — `hvigorw assembleApp` with `-p enableSignTask=false` and empty `signingConfigs` 2. **Sign separately** — `hap-sign-tool.jar` with plaintext passwords from GitHub Secrets See [`BUILD.md §5`](./BUILD.md#5-how-signing-works) for details. diff --git a/docs/BUILD.md b/docs/BUILD.md index f7443e8..1c33535 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -84,25 +84,31 @@ export KEY_ALIAS="electerm_key" The script performs two phases: -1. **Build unsigned HAP** — generates `build-profile.json5` (with empty `signingConfigs`), runs `ohpm install`, then `hvigorw assembleHap` with `-p enableSignTask=false`. +1. **Build unsigned APP** — generates `build-profile.json5` (with empty `signingConfigs`), runs `ohpm install`, then `hvigorw assembleApp` with `-p enableSignTask=false`. -2. **Sign the HAP** — invokes `hap-sign-tool.jar` directly with plaintext passwords to produce the signed `.hap` file. +2. **Sign the APP** — invokes `hap-sign-tool.jar` directly with plaintext passwords to produce the signed `.app` file. -The signed HAP is at: +The signed APP is at: ``` -entry/build/default/outputs/default/entry-default-unsigned.hap +build/outputs/default/electerm-harmony-default.app ``` -> The output filename contains "unsigned" in its name, but the file is signed — the signed version replaces the unsigned one in place. +> The `.app` package is a ZIP containing the HAP(s) + `pack.info`. This is the format required by AppGallery Connect for uploading. -### Step 6 — Install on device +### Step 6 — Install on device or upload to AGC + +**Upload to AppGallery Connect:** + +Upload the `.app` file directly in the AGC console under your app's testing section. + +**Install on device:** ```bash # Connect your HarmonyOS device via USB hdc list -# Install the HAP -hdc install entry/build/default/outputs/default/entry-default-unsigned.hap +# Install the APP +hdc install build/outputs/default/electerm-harmony-default.app ``` --- @@ -131,9 +137,9 @@ The workflow runs on: 8. Configure ohpm registry 9. Decode signing materials from GitHub Secrets → signing/ 10. Configure bundle name from secret (injected into app.json5) -11. Build unsigned HAP (hvigorw assembleHap with enableSignTask=false) -12. Sign HAP with hap-sign-tool.jar (plaintext passwords from secrets) -13. Upload .hap as GitHub Actions artifact (retained 30 days) +11. Build unsigned APP (hvigorw assembleApp with enableSignTask=false) +12. Sign APP with hap-sign-tool.jar (plaintext passwords from secrets) +13. Upload .app as GitHub Actions artifact (retained 30 days) 14. Write build summary ``` @@ -156,7 +162,7 @@ gh workflow run build.yml --ref dev gh workflow run build.yml --ref dev -f build_mode=debug ``` -Or use the GitHub Actions UI: **Actions** tab → **Build HarmonyOS HAP** → **Run workflow**. +Or use the GitHub Actions UI: **Actions** tab → **Build HarmonyOS APP** → **Run workflow**. --- @@ -168,7 +174,7 @@ The following scripts automate the build steps: |--------|---------| | [`scripts/prepare-node.sh`](../scripts/prepare-node.sh) | Downloads and extracts ohos-node prebuilt binary into `rawfile/node/` | | [`scripts/prepare-web.sh`](../scripts/prepare-web.sh) | Clones, builds, and bundles electerm-web into `rawfile/electerm-web/` | -| [`scripts/build-app.sh`](../scripts/build-app.sh) | Builds unsigned HAP with hvigorw, then signs it with `hap-sign-tool.jar` | +| [`scripts/build-app.sh`](../scripts/build-app.sh) | Builds unsigned APP with hvigorw assembleApp, then signs it with `hap-sign-tool.jar` | | [`scripts/gen-secrets.sh`](../scripts/gen-secrets.sh) | Generates GitHub Secrets values from `signing/` files and `temp/.env` | Run them in order for a local build: @@ -185,7 +191,7 @@ Run them in order for a local build: This project uses a **two-phase signing approach** instead of the standard DevEco Studio workflow: -### Phase 1: Build unsigned HAP +### Phase 1: Build unsigned APP `build-app.sh` generates a `build-profile.json5` with an **empty** `signingConfigs` array: @@ -205,7 +211,7 @@ This project uses a **two-phase signing approach** instead of the standard DevEc } ``` -Then builds with `-p enableSignTask=false` to skip the hvigor plugin's built-in signer. +Then builds with `-p enableSignTask=false` to skip the hvigor plugin's built-in signer. The `hvigorw assembleApp` command produces a `.app` package (ZIP containing HAP + `pack.info`). ### Phase 2: Sign with hap-sign-tool.jar @@ -218,11 +224,11 @@ java -jar hap-sign-tool.jar sign-app \ -keyPwd \ -appCertFile signing/electerm_publish.cer \ -profileFile signing/electermRelease.p7b \ - -inFile entry-default-unsigned.hap \ + -inFile electerm-harmony-default.app \ -signAlg SHA256withECDSA \ -keystoreFile signing/electerm.p12 \ -keystorePwd \ - -outFile entry-default-signed.hap + -outFile electerm-harmony-default-signed.app ``` ### Why not use hvigor's built-in signer? @@ -310,12 +316,12 @@ fnm use 24 ## 7. File Size Considerations -The final `.hap` package is approximately **230 MB** because it bundles: +The final `.app` package is approximately **230 MB** because it bundles: - ohos-node binary (~50 MB) - electerm-web server code + node_modules (~170 MB) -If the HAP needs to be smaller: +If the APP needs to be smaller: - Strip the node binary: `strip entry/src/main/resources/rawfile/node/bin/node` - Prune devDependencies from electerm-web: `npm prune --production` diff --git a/scripts/build-app.sh b/scripts/build-app.sh index 256e91b..c6dc050 100755 --- a/scripts/build-app.sh +++ b/scripts/build-app.sh @@ -1,11 +1,14 @@ #!/usr/bin/env bash -# build-app.sh — Build and sign the HarmonyOS HAP package. +# build-app.sh — Build and sign the HarmonyOS APP package. # -# This script builds an UNSIGNED HAP using hvigorw, then signs it -# directly using hap-sign-tool.jar with plaintext passwords. +# This script builds an UNSIGNED .app using hvigorw assembleApp, then signs +# it directly using hap-sign-tool.jar with plaintext passwords. # This bypasses the hvigor plugin's password encryption requirement # (which needs DevEco Studio's encrypted passwords + material/ key dirs). # +# The .app package is a ZIP containing the HAP(s) + pack.info, and is the +# format required by AppGallery Connect for uploading. +# # Prerequisites: # - HarmonyOS Command Line Tools installed (ohpm, hvigorw in PATH) # - Signing materials in signing/ directory @@ -121,7 +124,7 @@ echo " hvigorw: ${HVIGORW}" echo " sign tool: ${SIGN_TOOL_JAR}" # --- Generate build-profile.json5 (without signing config) ------------------ -# We build an UNSIGNED HAP and sign it separately with hap-sign-tool.jar. +# We build an UNSIGNED APP and sign it separately with hap-sign-tool.jar. # This avoids the hvigor plugin's password encryption requirement. echo "==> Configuring build-profile.json5 ..." @@ -216,43 +219,51 @@ echo "==> Installing ohpm dependencies ..." cd "${PROJECT_ROOT}" "${OHPM}" install -# --- Build the unsigned HAP ------------------------------------------------- +# --- Build the unsigned APP ------------------------------------------------- -echo "==> Building unsigned HAP (${BUILD_MODE}) ..." +echo "==> Building unsigned APP (${BUILD_MODE}) ..." # Build with enableSignTask=false to skip the hvigor signing step. # We sign separately using hap-sign-tool.jar with plaintext passwords. +# assembleApp produces a .app package (ZIP containing HAP + pack.info). if [ "${BUILD_MODE}" = "debug" ]; then - "${HVIGORW}" assembleHap --mode module -p product=default \ + "${HVIGORW}" assembleApp -p product=default \ -p buildMode=debug -p enableSignTask=false --no-daemon else - "${HVIGORW}" assembleHap --mode module -p product=default \ + "${HVIGORW}" assembleApp -p product=default \ -p buildMode=release -p enableSignTask=false --no-daemon fi -# --- Locate the unsigned HAP ------------------------------------------------ +# --- Locate the unsigned APP ------------------------------------------------ -HAP_DIR="${PROJECT_ROOT}/entry/build/default/outputs/default" -UNSIGNED_HAP=$(find "${HAP_DIR}" -name "*.hap" -type f | head -1) +# assembleApp outputs to build/outputs/default/*.app (project root level) +APP_DIR="${PROJECT_ROOT}/build/outputs/default" +UNSIGNED_APP=$(find "${APP_DIR}" -name "*.app" -type f 2>/dev/null | head -1) -if [ -z "${UNSIGNED_HAP}" ]; then - echo " ✗ No .hap file found in ${HAP_DIR}" - exit 1 +if [ -z "${UNSIGNED_APP}" ]; then + echo " ✗ No .app file found in ${APP_DIR}" + echo " Searching entire build tree ..." + UNSIGNED_APP=$(find "${PROJECT_ROOT}/build" -name "*.app" -type f 2>/dev/null | head -1) + if [ -z "${UNSIGNED_APP}" ]; then + echo " ✗ No .app file found anywhere in build/" + exit 1 + fi fi -echo " ✓ Unsigned HAP: ${UNSIGNED_HAP} ($(du -h "${UNSIGNED_HAP}" | cut -f1))" +echo " ✓ Unsigned APP: ${UNSIGNED_APP} ($(du -h "${UNSIGNED_APP}" | cut -f1))" -# --- Sign the HAP with hap-sign-tool.jar ------------------------------------ +# --- Sign the APP with hap-sign-tool.jar ------------------------------------ -echo "==> Signing HAP with hap-sign-tool.jar ..." +echo "==> Signing APP with hap-sign-tool.jar ..." # Show Java version for debugging (PKCS12 keystore compatibility) JAVA_VERSION=$(java -version 2>&1 | head -1) echo " Java: ${JAVA_VERSION}" -SIGNED_HAP="${UNSIGNED_HAP%.hap}-signed.hap" +SIGNED_APP="${UNSIGNED_APP%.app}-signed.app" -# Sign the HAP. +# Sign the APP package. +# hap-sign-tool.jar can sign both .hap and .app files. # NOTE: -mode localSign is required (COMMAND_ERROR code 101 if missing). # NOTE: -compatibleVersion, -signCode, -pwdInputMode are NOT supported by # this SDK version (COMMAND_PARAM_ERROR code 110). They were added later. @@ -262,29 +273,29 @@ java -jar "${SIGN_TOOL_JAR}" sign-app \ -keyPwd "${KEY_PASSWORD}" \ -appCertFile "${CERT_PATH}" \ -profileFile "${PROFILE_PATH}" \ - -inFile "${UNSIGNED_HAP}" \ + -inFile "${UNSIGNED_APP}" \ -signAlg SHA256withECDSA \ -keystoreFile "${KEYSTORE_PATH}" \ -keystorePwd "${KEYSTORE_PASSWORD}" \ - -outFile "${SIGNED_HAP}" + -outFile "${SIGNED_APP}" -if [ ! -f "${SIGNED_HAP}" ]; then - echo " ✗ Signing failed — no signed HAP produced" +if [ ! -f "${SIGNED_APP}" ]; then + echo " ✗ Signing failed — no signed APP produced" exit 1 fi -# Replace the unsigned HAP with the signed one -mv -f "${SIGNED_HAP}" "${UNSIGNED_HAP}" -HAP_FILE="${UNSIGNED_HAP}" +# Replace the unsigned APP with the signed one +mv -f "${SIGNED_APP}" "${UNSIGNED_APP}" +APP_FILE="${UNSIGNED_APP}" -echo " ✓ Signed HAP: ${HAP_FILE} ($(du -h "${HAP_FILE}" | cut -f1))" +echo " ✓ Signed APP: ${APP_FILE} ($(du -h "${APP_FILE}" | cut -f1))" # --- Done ------------------------------------------------------------------- echo "" echo "==> Build complete!" echo " Mode: ${BUILD_MODE}" -echo " HAP: ${HAP_FILE}" -echo " Size: $(du -h "${HAP_FILE}" | cut -f1)" +echo " APP: ${APP_FILE}" +echo " Size: $(du -h "${APP_FILE}" | cut -f1)" echo "" -echo " Install with: hdc install \"${HAP_FILE}\"" +echo " Upload to AppGallery Connect, or install with: hdc install \"${APP_FILE}\""