# HarmonyOS HAP build pipeline (OpenHarmony SDK, no DevEco required) $root = 'c:\Users\shz73\Documents\Qoder\2026-08-09\chat-1\device-management-system' $env:Path = 'C:\Program Files\nodejs;' + $env:Path $ets = "$root\harmonyos-tools\sdk\windows\ets\ets\build-tools\ets-loader" $proj = "$root\harmony-app\entry\src\main" $build = "$root\harmony-app\build" if (Test-Path "$build\cache") { Remove-Item -Recurse -Force "$build\cache" } New-Item -ItemType Directory -Force -Path "$build", "$build\intermediates", "$build\cache" | Out-Null Write-Host '=== webpack ets compile ===' $env:compileMode = 'esmodule' $env:buildMode = 'debug' $env:compilerType = 'ark' $env:cachePath = "$build\cache" $env:aceBuildJson = "$root\harmony-app\build.json" $env:aceModuleJsonPath = "$proj\module.json" $env:aceProfilePath = "$proj\resources\base\profile" $env:aceModuleRoot = "$proj\ets" $env:aceModuleBuild = "$build\intermediates" Push-Location $ets & 'C:\Program Files\nodejs\node.exe' "$ets\node_modules\webpack\bin\webpack.js" --config "$ets\webpack.debug.config.js" --env buildMode=debug --env compilerType=ark --env compileMode=esmodule --env aceModuleRoot=$proj\ets --env aceModuleBuild="$build\intermediates" --env aceModuleJsonPath="$proj\module.json" --env aceBuildJson="$root\harmony-app\build.json" --env aceProfilePath="$proj\resources\base\profile" --env cachePath="$build\cache" Write-Host "webpack exit: $LASTEXITCODE" Pop-Location if ($LASTEXITCODE -ne 0) { Write-Host 'BUILD FAILED at webpack step'; exit 1 } $tc = "$root\harmonyos-tools\sdk\windows\toolchains\toolchains" $lib = "$tc\lib" $jdk = "$root\android-tools\jdk\jdk-17.0.20+8\bin" $bundle = 'com.yingshi.devicemgr' $finalHap = "$root\harmony-app\影视星河设备管理系统.hap" Write-Host '=== restool compile resources ===' if (Test-Path "$build\res") { Remove-Item -Recurse -Force "$build\res" } New-Item -ItemType Directory -Force -Path "$build\res" | Out-Null & "$tc\restool.exe" -i $proj -o "$build\res" -p entry -r "$build\res\ResourceTable.txt" Write-Host "restool exit: $LASTEXITCODE" if ($LASTEXITCODE -ne 0) { exit 1 } Write-Host '=== assemble hap stage ===' $stage = "$build\hap-stage" if (Test-Path $stage) { Remove-Item -Recurse -Force $stage } New-Item -ItemType Directory -Force -Path $stage | Out-Null Copy-Item "$build\intermediates\modules.abc" "$stage\modules.abc" $mj = Get-Content "$build\res\module.json" -Raw | ConvertFrom-Json $mj.PSObject.Properties.Remove('app') [System.IO.File]::WriteAllText("$stage\module.json", ($mj | ConvertTo-Json -Depth 32), [System.Text.UTF8Encoding]::new($false)) Copy-Item "$build\res\resources.index" "$stage\resources.index" Copy-Item "$build\res\resources" "$stage\resources" -Recurse $packInfo = @{ summary = @{ app = @{ bundleName = $bundle debug = $true icon = '$media:icon' label = '$string:app_name' version = @{ code = 1000000; name = '1.0.0' } minAPIVersion = 9 targetAPIVersion = 12 } modules = @(@{ name = 'entry' type = 'entry' deviceType = @('phone', 'tablet') abilities = @(@{ name = 'EntryAbility'; label = '$string:EntryAbility_label'; visible = $true }) distro = @{ moduleType = 'entry'; installationFree = $false; deliveryWithInstall = $true; moduleName = 'entry' } }) } packages = @(@{ deviceType = @('phone', 'tablet'); moduleType = 'entry'; deliveryWithInstall = $true; name = 'entry' }) } [System.IO.File]::WriteAllText("$stage\pack.info", ($packInfo | ConvertTo-Json -Depth 32), [System.Text.UTF8Encoding]::new($false)) Write-Host '=== zip unsigned hap ===' Push-Location $stage & "$jdk\jar.exe" -cfM "$build\unsigned.hap" modules.abc module.json pack.info resources.index resources Write-Host "jar exit: $LASTEXITCODE" Pop-Location if ($LASTEXITCODE -ne 0) { exit 1 } Write-Host '=== prepare debug profile ===' $prof = Get-Content "$lib\UnsgnedDebugProfileTemplate.json" -Raw | ConvertFrom-Json $prof.uuid = [guid]::NewGuid().ToString() $prof.validity.'not-after' = 1893456000 $prof.'bundle-info'.'bundle-name' = $bundle [System.IO.File]::WriteAllText("$build\unsigned-profile.json", ($prof | ConvertTo-Json -Depth 32), [System.Text.UTF8Encoding]::new($false)) & "$jdk\keytool.exe" -exportcert -rfc -keystore "$lib\OpenHarmony.p12" -storetype PKCS12 -storepass 123456 -alias 'openharmony application ca' -file "$build\app-ca.cer" & "$jdk\keytool.exe" -exportcert -rfc -keystore "$lib\OpenHarmony.p12" -storetype PKCS12 -storepass 123456 -alias 'openharmony application root ca' -file "$build\app-root.cer" $chain = $prof.'bundle-info'.'development-certificate' + (Get-Content "$build\app-ca.cer" -Raw) + (Get-Content "$build\app-root.cer" -Raw) [System.IO.File]::WriteAllText("$build\app-cert.pem", $chain, [System.Text.UTF8Encoding]::new($false)) Write-Host '=== sign profile ===' & "$jdk\java.exe" -jar "$lib\hap-sign-tool.jar" sign-profile -mode localSign -keyAlias 'openharmony application profile debug' -keyPwd 123456 -profileCertFile "$lib\OpenHarmonyProfileDebug.pem" -inFile "$build\unsigned-profile.json" -signAlg SHA256withECDSA -keystoreFile "$lib\OpenHarmony.p12" -keystorePwd 123456 -outFile "$build\debug-profile.p7b" Write-Host "sign-profile exit: $LASTEXITCODE" if ($LASTEXITCODE -ne 0) { exit 1 } Write-Host '=== sign hap ===' & "$jdk\java.exe" -jar "$lib\hap-sign-tool.jar" sign-app -mode localSign -keyAlias 'openharmony application release' -keyPwd 123456 -appCertFile "$build\app-cert.pem" -profileFile "$build\debug-profile.p7b" -inFile "$build\unsigned.hap" -signAlg SHA256withECDSA -keystoreFile "$lib\OpenHarmony.p12" -keystorePwd 123456 -outFile $finalHap -compatibleVersion 9 -inForm zip Write-Host "sign-app exit: $LASTEXITCODE" if ($LASTEXITCODE -ne 0) { exit 1 } Write-Host '=== verify ===' & "$jdk\java.exe" -jar "$lib\hap-sign-tool.jar" verify-app -inFile $finalHap -outCertChain "$build\verify-certchain.cer" -outProfile "$build\verify-profile.p7b" Write-Host "verify exit: $LASTEXITCODE" Write-Host 'DONE' Get-Item $finalHap | Select-Object FullName, Length