| name: OpenHarmony |
| on: |
| workflow_call: |
| inputs: |
| profile: |
| required: false |
| default: "release" |
| type: string |
| upload: |
| required: false |
| default: false |
| type: boolean |
| github-release-id: |
| required: false |
| type: string |
| bencher: |
| required: false |
| default: false |
| type: boolean |
| |
| workflow_dispatch: |
| inputs: |
| profile: |
| required: false |
| default: "release" |
| type: choice |
| description: "Cargo build profile" |
| options: ["release", "debug", "production"] |
| bencher: |
| required: false |
| default: false |
| type: boolean |
| |
| env: |
| RUST_BACKTRACE: 1 |
| SHELL: /bin/bash |
| CARGO_INCREMENTAL: 0 |
| BENCHER_PROJECT: ${{ vars.BENCHER_PROJECT || 'servo' }} |
| HITRACE_BENCH_VERSION: 0.8.2 |
| |
| jobs: |
| build: |
| name: OpenHarmony Build |
| runs-on: ubuntu-22.04 |
| strategy: |
| matrix: |
| target: ["aarch64-unknown-linux-ohos", "x86_64-unknown-linux-ohos"] |
| outputs: |
| signed: ${{ steps.signing_config.outputs.signed }} |
| steps: |
| - uses: actions/checkout@v4 |
| if: github.event_name != 'pull_request_target' |
| with: |
| fetch-depth: 2 |
| # This is necessary to checkout the pull request if this run was triggered via a |
| # `pull_request_target` event. |
| - uses: actions/checkout@v4 |
| if: github.event_name == 'pull_request_target' |
| with: |
| ref: ${{ github.event.pull_request.head.sha }} |
| fetch-depth: 2 |
| - name: Install crown |
| run: cargo install --path support/crown |
| - name: Setup Python |
| uses: ./.github/actions/setup-python |
| - name: Bootstrap dependencies |
| run: sudo apt update && ./mach bootstrap --skip-lints |
| - name: Setup OpenHarmony SDK |
| id: setup_sdk |
| uses: openharmony-rs/setup-ohos-sdk@v0.2.3 |
| with: |
| version: "5.0.2" |
| fixup-path: true |
| - name: Install node for hvigor |
| uses: actions/setup-node@v4 |
| with: |
| node-version: 18 |
| - name: Install hvigor modules |
| run: | |
| mkdir ~/hvigor-installation |
| cd ~/hvigor-installation |
| echo "@ohos:registry=https://repo.harmonyos.com/npm/" > .npmrc |
| npm install "@ohos/hvigor@5" "@ohos/hvigor-ohos-plugin@5" |
| echo "HVIGOR_PATH=$PWD" >> $GITHUB_ENV |
| - name: "Setup HAP signing config" |
| id: signing_config |
| env: |
| SIGNING_MATERIAL: ${{ secrets.SERVO_OHOS_SIGNING_MATERIAL }} |
| if: ${{ inputs.upload || env.SIGNING_MATERIAL != '' }} # Allows the build to pass on forks. |
| run: | |
| cd ~ |
| echo "${SIGNING_MATERIAL}" | base64 -d > servo-ohos-material.zip |
| unzip servo-ohos-material.zip |
| echo "SERVO_OHOS_SIGNING_CONFIG=${PWD}/servo-ohos-material/signing-configs.json" >> $GITHUB_ENV |
| echo "signed=true" >> "$GITHUB_OUTPUT" |
| - name: Build (arch ${{ matrix.target }} profile ${{ inputs.profile }}) |
| env: |
| OHOS_SDK_NATIVE: ${{ steps.setup_sdk.outputs.ohos_sdk_native }} |
| OHOS_BASE_SDK_HOME: ${{ steps.setup_sdk.outputs.ohos-base-sdk-home }} |
| run: | |
| ./mach build --locked --target ${{ matrix.target }} --${{ inputs.profile }} |
| cp -r target/cargo-timings target/cargo-timings-ohos-${{ matrix.target }} |
| - name: Archive build timing |
| uses: actions/upload-artifact@v4 |
| with: |
| name: cargo-timings-ohos-${{ matrix.target }}-${{ inputs.profile }} |
| # Using a wildcard here ensures that the archive includes the path. |
| path: target/cargo-timings-* |
| - name: Upload nightly |
| if: ${{ inputs.upload && contains(matrix.target, 'aarch64') }} |
| run: | |
| ./mach upload-nightly ohos \ |
| --secret-from-environment \ |
| --github-release-id ${{ inputs.github-release-id }} |
| env: |
| S3_UPLOAD_CREDENTIALS: ${{ secrets.S3_UPLOAD_CREDENTIALS }} |
| NIGHTLY_REPO_TOKEN: ${{ secrets.NIGHTLY_REPO_TOKEN }} |
| NIGHTLY_REPO: ${{ github.repository_owner }}/servo-nightly-builds |
| - name: Generate artifact attestation for HAP |
| if: ${{ inputs.upload }} |
| uses: actions/attest-build-provenance@v1 |
| with: |
| subject-path: target/openharmony/${{ matrix.target }}/${{ inputs.profile }}/entry/build/default/outputs/default/servoshell-default-signed.hap |
| - name: Upload signed HAP artifact |
| if: ${{ env.SERVO_OHOS_SIGNING_CONFIG != '' }} # Build output has different name if not signed. |
| uses: actions/upload-artifact@v4 |
| with: |
| name: ${{ inputs.profile }}-binary-ohos-${{ matrix.target }} |
| path: target/openharmony/${{ matrix.target }}/${{ inputs.profile }}/entry/build/default/outputs/default/servoshell-default-signed.hap |
| - name: Upload unsigned HAP artifact |
| if: ${{ env.SERVO_OHOS_SIGNING_CONFIG == '' }} # Build output has different name if not signed. |
| uses: actions/upload-artifact@v4 |
| with: |
| name: ${{ inputs.profile }}-binary-ohos-${{ matrix.target }} |
| path: target/openharmony/${{ matrix.target }}/${{ inputs.profile }}/entry/build/default/outputs/default/servoshell-default-unsigned.hap |
| |
| bencher: |
| needs: ["build"] |
| strategy: |
| matrix: |
| target: ["aarch64-unknown-linux-ohos", "x86_64-unknown-linux-ohos"] |
| if: ${{ inputs.bencher && inputs.profile != 'debug' && github.event_name != 'workflow_dispatch' && github.event_name != 'merge_group' }} |
| uses: ./.github/workflows/bencher.yml |
| with: |
| target: ohos-${{ matrix.target }} |
| profile: ${{ inputs.profile }} |
| compressed-file-path: ${{ inputs.profile }}-binary-ohos-${{ matrix.target }}/servoshell-default-${{ needs.build.outputs.signed && 'signed' || 'unsigned' }}.hap |
| binary-path: libs/${{ matrix.target == 'aarch64-unknown-linux-ohos' && 'arm64-v8a' || matrix.target == 'x86_64-unknown-linux-ohos' && 'x86_64' }}/libservoshell.so |
| file-size: true |
| speedometer: false |
| dromaeo: false |
| secrets: inherit |
| |
| # Note: We could potentially also merge this build job with the above one, |
| # if we figure out how to make hvigor build for harmonyos without the HOS commandline-tools installed. |
| build-harmonyos-aarch64: |
| name: HarmonyOS Build (aarch64) |
| continue-on-error: true |
| runs-on: hos-builder |
| if: github.repository == 'servo/servo' |
| steps: |
| - if: ${{ github.event_name != 'pull_request_target' }} |
| run: git fetch --depth=1 origin $GITHUB_SHA |
| - if: ${{ github.event_name == 'pull_request_target' }} |
| run: git fetch --depth=1 origin ${{ github.event.pull_request.head.sha }} |
| - run: | |
| git switch --detach |
| git reset --hard FETCH_HEAD |
| - name: Adding test files to directory |
| run: cp -r support/hitrace-bencher/* support/openharmony/AppScope/resources/resfile/ |
| - name: Build for aarch64 HarmonyOS |
| run: | |
| ./mach build --locked --target aarch64-unknown-linux-ohos --profile=${{ inputs.profile }} --flavor=harmonyos --no-default-features --features tracing,tracing-hitrace |
| - uses: actions/upload-artifact@v4 |
| with: |
| # Upload the **unsigned** artifact - We don't have the signing materials in pull request workflows |
| name: servoshell-hos-${{ inputs.profile }}.hap |
| path: target/openharmony/aarch64-unknown-linux-ohos/${{ inputs.profile }}/entry/build/harmonyos/outputs/default/servoshell-default-unsigned.hap |
| |
| test-harmonyos-aarch64: |
| name: Test HarmonyOS aarch64 |
| # Don't block servos Merge queue on this job failing. |
| # Since we just added this, there might be some hidden issues, |
| # so in the beginning we will just do a best effort approach but ignore errors. |
| continue-on-error: true |
| runs-on: hos-runner |
| if: github.repository == 'servo/servo' |
| needs: build-harmonyos-aarch64 |
| steps: |
| - uses: actions/download-artifact@v4 |
| with: |
| # Name of the artifact to download. |
| # If unspecified, all artifacts for the run are downloaded. |
| name: servoshell-hos-${{ inputs.profile }}.hap |
| - name: Test hdc device |
| # First we ensure a device is actually connected and working. |
| run: hdc list targets && hdc shell echo hello world |
| - name: Sign the hap |
| run: | |
| ls -la |
| /usr/bin/sign-hos.sh servoshell-default-unsigned.hap servoshell-hos-signed.hap |
| - name: Install |
| run: | |
| # Uninstall first. hdc is not very reliable in terms of exiting with an error, so we uninstall first |
| # to make sure we don't use a previous version if installation failed for some reason. |
| hdc uninstall org.servo.servo |
| hdc install -r servoshell-hos-signed.hap |
| - name: Test loading servo.org |
| env: |
| TRACE_BUFFER_SZ_KB: "524288" # 512 MB |
| run: | |
| mkdir test_output |
| hdc shell aa force-stop org.servo.servo |
| # Hitrace allows us to save application and system traces, which is useful to analyze performance. |
| # The main reason however, is that we can use the application traces to determine if servo |
| # successfully reaches certain locations in the code, in particular if a page is successfully loaded. |
| hdc shell hitrace -b "${TRACE_BUFFER_SZ_KB}" app graphic ohos freq idle memory --trace_begin |
| # We start servo, tell it to load a website (servo.org). JIT is not allowed on HarmonyOS 5. |
| hdc shell aa start -a EntryAbility -b org.servo.servo -U https://servo.org --ps=--pref js_disable_jit=true |
| servo_pid=$(hdc shell pidof org.servo.servo) |
| # We don't really know how long servo needs to load a webpage, so we just wait 10s. |
| sleep 10 |
| # We dump the trace in ftrace format to disk |
| hdc shell hitrace -b "${TRACE_BUFFER_SZ_KB}" --trace_finish -o /data/local/tmp/ohtrace.txt |
| hdc shell snapshot_display -f /data/local/tmp/servo.jpeg |
| hdc file recv /data/local/tmp/servo.jpeg test_output/servo_hos_screenshot.jpeg |
| hdc file recv /data/local/tmp/ohtrace.txt test_output/servo.ftrace |
| # To limit the logsize we only save logs from servo. |
| hdc shell hilog --exit -D 0xE0C3 > test_output/servo.log |
| # todo: Also benchmark some other websites.... |
| - name: Upload artifacts |
| uses: actions/upload-artifact@v4 |
| with: |
| path: test_output |
| name: hos-${{ inputs.profile }}-test-output |
| - name: Check success |
| run: | |
| # would be empty if servo crashed. |
| servo_pid=$(hdc shell pidof org.servo.servo) |
| [[ $servo_pid =~ ^[0-9]+$ ]] || { echo "It looks like servo crashed!" ; exit 1; } |
| # If the grep fails, then the trace output for the "page loaded" prompt is missing |
| grep 'org\.servo\.servo-.* tracing_mark_write.*PageLoadEndedPrompt' test_output/servo.ftrace |
| |
| bench-harmonyos-aarch64: |
| name: Benching HarmonyOS aarch64 |
| continue-on-error: true |
| runs-on: hos-runner |
| needs: test-harmonyos-aarch64 |
| if: github.repository == 'servo/servo' |
| steps: |
| - uses: actions/download-artifact@v4 |
| with: |
| # Name of the artifact to download. |
| # If unspecified, all artifacts for the run are downloaded. |
| name: servoshell-hos-${{ inputs.profile }}.hap |
| - name: Test hdc device |
| # First we ensure a device is actually connected and working. |
| run: hdc list targets && hdc shell echo hello world |
| - name: Sign the hap |
| run: | |
| ls -la |
| /usr/bin/sign-hos.sh servoshell-default-unsigned.hap servoshell-hos-signed.hap |
| - name: Install |
| run: | |
| # Uninstall first. hdc is not very reliable in terms of exiting with an error, so we uninstall first |
| # to make sure we don't use a previous version if installation failed for some reason. |
| hdc uninstall org.servo.servo |
| hdc install -r servoshell-hos-signed.hap |
| - uses: actions/checkout@v4 |
| if: github.event_name != 'pull_request_target' |
| with: |
| fetch-depth: 0 |
| # This is necessary to checkout the pull request if this run was triggered via a |
| # `pull_request_target` event. |
| - uses: actions/checkout@v4 |
| if: github.event_name == 'pull_request_target' |
| with: |
| ref: ${{ github.event.pull_request.head.sha }} |
| fetch-depth: 0 |
| - name: Setup Python |
| uses: ./.github/actions/setup-python |
| - name: Getting hitrace-bench version |
| run: echo "INSTALLED_HITRACE_BENCH_VERSION=$(hitrace-bench --version | awk '{print $2}')" >> $GITHUB_ENV |
| - name: Install hitrace-bench |
| run: cargo install --locked hitrace-bench --version $HITRACE_BENCH_VERSION |
| if: ${{ env.INSTALLED_HITRACE_BENCH_VERSION != env.HITRACE_BENCH_VERSION }} |
| - name: "Run benchmark" |
| run: hitrace-bench -r support/hitrace-bencher/runs.json --bencher -p ${{ inputs.profile }} |
| continue-on-error: true |
| - name: Run speedometer |
| id: BencherRun |
| run: python3 ./mach test-speedometer-ohos --bmf-output speedometer.json --profile ${{ inputs.profile }} |
| continue-on-error: true |
| - name: Create empty speedometer.json if failed |
| run: touch speedometer.json |
| if: ${{ steps.BencherRun.outcome == 'failure' }} |
| - name: Getting model name |
| # awk is used to remove trailing spaces |
| run: | |
| echo "MODEL_NAME=$(hdc shell param get const.product.name | awk '{$1=$1;print}' )" >> $GITHUB_ENV |
| - name: Combining bencher files |
| run: jq --compact-output --slurp add speedometer.json bench.json > combined-bencher.json |
| - uses: bencherdev/bencher@main |
| # set options |
| - name: Set bencher opts for PRs (label try run) |
| if: github.event_name == 'pull_request_target' |
| run: | |
| echo "RUN_BENCHER_OPTIONS=--branch ${{ github.event.number }}/PR \ |
| --start-point ${{ github.base_ref }} \ |
| --start-point-hash ${{ github.event.pull_request.base.sha }} \ |
| --start-point-reset \ |
| --start-point-clone-thresholds" >> "$GITHUB_ENV" |
| - name: Set bencher opts for main |
| if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} |
| run: | |
| echo "RUN_BENCHER_OPTIONS=--branch main" >> "$GITHUB_ENV" |
| - name: Set bencher opts for try branch |
| if: ${{ github.event_name == 'push' && github.ref_name == 'try' }} |
| run: | |
| git remote add upstream https://github.com/servo/servo |
| git fetch upstream main |
| echo "RUN_BENCHER_OPTIONS=--branch try \ |
| --hash $(git rev-parse HEAD~1) \ |
| --start-point main \ |
| --start-point-hash $(git merge-base upstream/main HEAD) \ |
| --start-point-clone-thresholds \ |
| --start-point-reset" >> "$GITHUB_ENV" |
| - name: Uploading to bencher.dev |
| # Note: That e.g. `--start-point` is ignored if it is an empty string, |
| # which should be the case on e.g. normal push workflows on main. |
| run: | |
| bencher run --adapter json \ |
| --file combined-bencher.json \ |
| --project '${{ env.BENCHER_PROJECT }}' \ |
| --token '${{ secrets.BENCHER_API_TOKEN }}' \ |
| --github-actions '${{ secrets.GITHUB_TOKEN }}' \ |
| --testbed="$MODEL_NAME" \ |
| $RUN_BENCHER_OPTIONS |
| - name: Success |
| if: ${{ !contains(steps.*.outcome, 'failure') && !contains(steps.*.outcome, 'cancelled') }} |
| run: exit 0 |
| - name: Failure |
| if: contains(steps.*.outcome, 'failure') || contains(steps.*.outcome, 'cancelled') |
| run: exit 1 |