Repository navigation
fix(core): flank strikethrough tildes like asterisk emphasis, matching GitHub #26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Android benchmarks | |
| # Label a pull request `benchmark: android` (later pushes re-run it while the | |
| # label stays), comment `/benchmark android` on it, or dispatch with its number, | |
| # to compare its head against main on one emulator and post the table. | |
| # Forks run only at a reviewed commit: `/benchmark android <full head SHA>`. | |
| on: | |
| pull_request: | |
| types: [labeled, synchronize] | |
| issue_comment: | |
| types: [created] | |
| workflow_dispatch: | |
| inputs: | |
| pull_request: | |
| description: Pull request number | |
| required: true | |
| type: number | |
| commit: | |
| description: Full SHA of the head commit you reviewed (required for a pull request from a fork) | |
| required: false | |
| type: string | |
| jobs: | |
| resolve: | |
| # Runs pull request code, so it needs write access. The association is a | |
| # cheap prefilter; the permission check below decides. The label skips | |
| # forks, which have no reviewed commit to pin and get a read-only token. | |
| if: >- | |
| (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && ( | |
| (github.event.action == 'labeled' && github.event.label.name == 'benchmark: android') | |
| || (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'benchmark: android')))) | |
| || (github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/benchmark android') && contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) | |
| || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: read | |
| outputs: | |
| number: ${{ steps.pr.outputs.number }} | |
| head: ${{ steps.pr.outputs.head }} | |
| base: ${{ steps.pr.outputs.base }} | |
| behind: ${{ steps.pr.outputs.behind }} | |
| steps: | |
| # Labeling needs only triage access, so the labeler is checked too. | |
| - name: Check the requester's permission | |
| if: github.event_name != 'workflow_dispatch' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REQUESTER: ${{ github.event.sender.login }} | |
| run: | | |
| set -euo pipefail | |
| PERMISSION=$(gh api "repos/$GITHUB_REPOSITORY/collaborators/$REQUESTER/permission" --jq .permission) | |
| # maintain reads as write here, and triage as read. | |
| if [[ "$PERMISSION" != admin && "$PERMISSION" != write ]]; then | |
| echo "::error::@$REQUESTER has $PERMISSION access; a benchmark needs write access" | |
| exit 1 | |
| fi | |
| - name: Acknowledge the comment | |
| if: github.event_name == 'issue_comment' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh api --method POST "repos/$GITHUB_REPOSITORY/issues/comments/${{ github.event.comment.id }}/reactions" \ | |
| -f content=rocket --silent || true | |
| # Pins both commits, so a push during the run changes nothing. A fork is | |
| # measured only at the commit the requester reviewed. | |
| - name: Resolve the commits | |
| id: pr | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| EVENT: ${{ github.event_name }} | |
| NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pull_request }} | |
| BODY: ${{ github.event.comment.body }} | |
| COMMIT: ${{ inputs.commit }} | |
| run: | | |
| set -euo pipefail | |
| refuse() { | |
| echo "::error::$1" | |
| if [[ "$EVENT" == issue_comment ]]; then | |
| gh api "repos/$GITHUB_REPOSITORY/issues/$NUMBER/comments" -f body="$1" --silent || true | |
| fi | |
| exit 1 | |
| } | |
| # `/benchmark android [commit]`, on the first line of the comment. | |
| REQUESTED=$COMMIT | |
| if [[ "$EVENT" == issue_comment ]]; then | |
| read -r _ _ REQUESTED _ <<< "$(head -n 1 <<< "$BODY" | tr -d '\r')" | |
| fi | |
| REQUESTED=${REQUESTED:-} | |
| if [[ -n "$REQUESTED" && ! "$REQUESTED" =~ ^[0-9a-f]{40}$ ]]; then | |
| refuse "Name the commit with its full 40-character SHA, as in \`/benchmark android <SHA>\`." | |
| fi | |
| PR=$(gh pr view "$NUMBER" --repo "$GITHUB_REPOSITORY" --json headRefOid,isCrossRepository \ | |
| --jq '"\(.headRefOid) \(.isCrossRepository)"') | |
| HEAD=${PR% *} | |
| FORK=${PR#* } | |
| BASE=$(gh api "repos/$GITHUB_REPOSITORY/commits/main" --jq .sha) | |
| if [[ -z "$HEAD" || -z "$BASE" ]]; then | |
| echo "::error::Could not resolve the head of pull request #$NUMBER or the tip of main" | |
| exit 1 | |
| fi | |
| if [[ -z "$REQUESTED" && "$FORK" == true ]]; then | |
| refuse "#$NUMBER comes from a fork, so the benchmark runs its code only at a commit you name. Review the head, then comment \`/benchmark android $HEAD\`." | |
| fi | |
| if [[ -n "$REQUESTED" && "$REQUESTED" != "$HEAD" ]]; then | |
| refuse "The head of #$NUMBER is now \`$HEAD\`, not the requested \`$REQUESTED\`. Review what changed, then request the benchmark at the new head." | |
| fi | |
| # Informational only: a head behind main also measures what main gained since. | |
| BEHIND=$(gh api "repos/$GITHUB_REPOSITORY/compare/$BASE...$HEAD" --jq .behind_by || echo "") | |
| { | |
| echo "number=$NUMBER" | |
| echo "head=$HEAD" | |
| echo "base=$BASE" | |
| echo "behind=$BEHIND" | |
| } >> "$GITHUB_OUTPUT" | |
| benchmark: | |
| needs: resolve | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 90 | |
| # A newer request cancels this one. Set on the job, not the workflow, so | |
| # only a request that passed resolve can cancel. | |
| concurrency: | |
| group: android-benchmarks-${{ needs.resolve.outputs.number }} | |
| cancel-in-progress: true | |
| # Runs the pull request's code, so the token can only read. | |
| permissions: | |
| contents: read | |
| steps: | |
| # Tooling comes from main; the pull request is built in a worktree, which | |
| # shares .git/config, so no credentials are persisted. | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| ref: ${{ needs.resolve.outputs.base }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| # Pull requests from forks are only reachable through their pull ref. | |
| - name: Fetch the pull request | |
| env: | |
| NUMBER: ${{ needs.resolve.outputs.number }} | |
| HEAD: ${{ needs.resolve.outputs.head }} | |
| run: | | |
| git fetch --no-tags origin "+refs/pull/$NUMBER/head:refs/remotes/pull/$NUMBER" | |
| git cat-file -e "$HEAD^{commit}" | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version-file: .nvmrc | |
| # Restore only: a cache saved from this run would reach later runs on main. | |
| - name: Setup Android | |
| uses: ./.github/actions/setup-android | |
| with: | |
| gradle-wrapper-properties: packages/enriched-markdown-android/gradle/wrapper/gradle-wrapper.properties | |
| cache: restore | |
| - name: Enable KVM | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| # ATD images run fewer background services, so there is less noise. The | |
| # screen stays unlocked; the benchmark library refuses a locked one. The | |
| # build runs without the Actions runtime token, so its tooling cannot | |
| # save caches to main by accident. Code that wants the token can still | |
| # read it from the runner, so reviewing what runs here is the safeguard. | |
| # A step that runs out of time fails, while a job that does is cancelled | |
| # and would read as superseded, so the limit is set here, under the job's. | |
| - name: Compare the pull request against main | |
| timeout-minutes: 75 | |
| uses: ReactiveCircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0 | |
| with: | |
| api-level: 35 | |
| target: aosp_atd | |
| arch: x86_64 | |
| profile: pixel_6 | |
| cores: 2 | |
| ram-size: 4096M | |
| script: | | |
| adb shell svc power stayon true | |
| adb shell input keyevent KEYCODE_WAKEUP | |
| adb shell wm dismiss-keyguard | |
| env -u ACTIONS_RUNTIME_TOKEN -u ACTIONS_RUNTIME_URL -u ACTIONS_CACHE_URL -u ACTIONS_RESULTS_URL node packages/enriched-markdown-android/display-benchmark/tools/compare.mjs --head ${{ needs.resolve.outputs.head }} --base ${{ needs.resolve.outputs.base }} --output "$RUNNER_TEMP/android-benchmarks" | |
| - name: Upload the results | |
| if: ${{ !cancelled() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: android-benchmarks | |
| path: ${{ runner.temp }}/android-benchmarks | |
| if-no-files-found: ignore | |
| # Kept apart so pull request code never holds a write token. A cancelled run | |
| # was superseded, so it leaves the report to the newer one. | |
| report: | |
| needs: [resolve, benchmark] | |
| if: ${{ !cancelled() && needs.resolve.result == 'success' && needs.benchmark.result != 'cancelled' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| steps: | |
| # The results came from pull request code, so main's script reads them as | |
| # data only. | |
| - name: Checkout the report script | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| ref: ${{ needs.resolve.outputs.base }} | |
| path: tooling | |
| persist-credentials: false | |
| sparse-checkout: | | |
| .nvmrc | |
| packages/enriched-markdown-android/display-benchmark/tools | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version-file: tooling/.nvmrc | |
| - name: Download the results | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: android-benchmarks | |
| path: results | |
| - name: Make the table | |
| id: table | |
| if: needs.benchmark.result == 'success' | |
| continue-on-error: true | |
| run: node tooling/packages/enriched-markdown-android/display-benchmark/tools/compare.mjs --report results --markdown table.md | |
| - name: Comment on the pull request | |
| env: | |
| BENCHMARK: ${{ needs.benchmark.result }} | |
| TABLE: ${{ steps.table.outcome }} | |
| GH_TOKEN: ${{ github.token }} | |
| NUMBER: ${{ needs.resolve.outputs.number }} | |
| HEAD: ${{ needs.resolve.outputs.head }} | |
| BASE: ${{ needs.resolve.outputs.base }} | |
| BEHIND: ${{ needs.resolve.outputs.behind }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| COMMIT_URL: ${{ github.server_url }}/${{ github.repository }}/commit | |
| run: | | |
| set -euo pipefail | |
| { | |
| echo "### Android display benchmark" | |
| echo | |
| echo "**head** #$NUMBER at [\`${HEAD:0:10}\`]($COMMIT_URL/$HEAD) · **base** \`main\` at [\`${BASE:0:10}\`]($COMMIT_URL/$BASE)" | |
| echo | |
| echo "> [!IMPORTANT]" | |
| echo "> Measured on an emulator on a shared CI runner, so the numbers only show the direction of a change. Confirm them on a physical device before relying on them; see the [display benchmark README](https://github.com/$GITHUB_REPOSITORY/blob/main/packages/enriched-markdown-android/display-benchmark/README.md#running)." | |
| echo | |
| if [[ -n "$BEHIND" && "$BEHIND" != "0" ]]; then | |
| echo "The pull request is $BEHIND commit(s) behind \`main\`, so the difference also includes what \`main\` gained since. Rebase it to measure its own changes alone." | |
| echo | |
| fi | |
| if [[ "$BENCHMARK" != success ]]; then | |
| echo "The benchmark did not finish, so there are no results to compare." | |
| elif [[ "$TABLE" != success ]]; then | |
| echo "The benchmark finished, but its results could not be read; see the run." | |
| else | |
| cat table.md | |
| fi | |
| echo | |
| echo "[Run]($RUN_URL)" | |
| } > comment.md | |
| cat comment.md >> "$GITHUB_STEP_SUMMARY" | |
| gh pr comment "$NUMBER" --repo "$GITHUB_REPOSITORY" --body-file comment.md |