|
| 1 | +# Copyright 2026 Google LLC |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | +name: Jetpack Compose Performance Review |
| 15 | + |
| 16 | +permissions: |
| 17 | + contents: read |
| 18 | + issues: write |
| 19 | + pull-requests: write |
| 20 | + |
| 21 | +on: |
| 22 | + pull_request: |
| 23 | + branches: [ '*' ] |
| 24 | + paths: |
| 25 | + - '**/*.kt' |
| 26 | + - '.github/workflows/compose_performance_skill.yml' |
| 27 | + workflow_dispatch: |
| 28 | + inputs: |
| 29 | + pr_number: |
| 30 | + description: 'Pull request number to review (leave empty to review the latest commit)' |
| 31 | + required: false |
| 32 | + type: string |
| 33 | + |
| 34 | +concurrency: |
| 35 | + group: ${{ github.workflow }}-${{ github.ref }} |
| 36 | + cancel-in-progress: true |
| 37 | + |
| 38 | +jobs: |
| 39 | + compose-performance-review: |
| 40 | + runs-on: ubuntu-latest |
| 41 | + timeout-minutes: 30 |
| 42 | + if: github.repository == 'android/compose-samples' && (github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.fork == false) |
| 43 | + |
| 44 | + steps: |
| 45 | + - name: Checkout Repository |
| 46 | + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
| 47 | + with: |
| 48 | + token: ${{ secrets.PAT || github.token }} |
| 49 | + fetch-depth: 0 |
| 50 | + persist-credentials: false |
| 51 | + |
| 52 | + - name: Prepare Review Context |
| 53 | + id: context |
| 54 | + env: |
| 55 | + GH_TOKEN: ${{ secrets.PAT || github.token }} |
| 56 | + PR_NUMBER: ${{ inputs.pr_number || github.event.pull_request.number }} |
| 57 | + BASE_REF: ${{ github.base_ref }} |
| 58 | + run: | |
| 59 | + set -euo pipefail |
| 60 | + if [[ -n "${PR_NUMBER}" ]]; then |
| 61 | + if [[ -z "${BASE_REF}" ]]; then |
| 62 | + BASE_REF=$(gh pr view "${PR_NUMBER}" --json baseRefName --jq '.baseRefName') |
| 63 | + gh pr checkout "${PR_NUMBER}" |
| 64 | + git fetch origin "${BASE_REF}" |
| 65 | + fi |
| 66 | + DIFF_RANGE="origin/${BASE_REF}...HEAD" |
| 67 | + echo "mode=pr" >> "${GITHUB_OUTPUT}" |
| 68 | + echo "pr_number=${PR_NUMBER}" >> "${GITHUB_OUTPUT}" |
| 69 | + else |
| 70 | + DIFF_RANGE="HEAD~1...HEAD" |
| 71 | + echo "mode=commit" >> "${GITHUB_OUTPUT}" |
| 72 | + echo "pr_number=" >> "${GITHUB_OUTPUT}" |
| 73 | + fi |
| 74 | + if [[ -f .gitignore ]]; then |
| 75 | + sed -i '/^\.gemini\/$/d; /^\.agents\/$/d' .gitignore |
| 76 | + fi |
| 77 | + mkdir -p .gemini |
| 78 | + git diff --name-only --diff-filter=ACMRT "${DIFF_RANGE}" -- '*.kt' > .gemini/target_files.txt |
| 79 | + git diff -U5 --diff-filter=ACMRT "${DIFF_RANGE}" -- '*.kt' > .gemini/pr_diff.patch |
| 80 | + FILE_COUNT=$(grep -c '\.kt$' .gemini/target_files.txt || true) |
| 81 | + echo "file_count=${FILE_COUNT}" >> "${GITHUB_OUTPUT}" |
| 82 | + echo "Found ${FILE_COUNT} modified Kotlin file(s) to audit." |
| 83 | +
|
| 84 | + - name: Install Jetpack Compose Performance Skill |
| 85 | + if: steps.context.outputs.file_count != '0' |
| 86 | + run: | |
| 87 | + set -euo pipefail |
| 88 | + curl -fsSL https://dl.google.com/android/cli/latest/linux_x86_64/install.sh | bash |
| 89 | + export PATH="${HOME}/.local/bin:${PATH}" |
| 90 | + android skills add --agent=gemini --project=. jetpack-compose-performance |
| 91 | +
|
| 92 | + - name: Run Jetpack Compose Performance Skill |
| 93 | + if: steps.context.outputs.file_count != '0' |
| 94 | + id: gemini_review |
| 95 | + uses: google-github-actions/run-gemini-cli@f77273f4c914e4bf38440cf36a0369cb64a37489 # v0.1.22 |
| 96 | + env: |
| 97 | + GEMINI_CLI_TRUST_WORKSPACE: 'true' |
| 98 | + GITHUB_TOKEN: ${{ secrets.PAT || github.token }} |
| 99 | + with: |
| 100 | + gemini_api_key: ${{ secrets.GEMINI_API_KEY }} |
| 101 | + github_pr_number: ${{ steps.context.outputs.pr_number }} |
| 102 | + upload_artifacts: 'true' |
| 103 | + settings: | |
| 104 | + { |
| 105 | + "model": { |
| 106 | + "maxSessionTurns": 50 |
| 107 | + }, |
| 108 | + "context": { |
| 109 | + "fileFiltering": { |
| 110 | + "respectGitIgnore": false, |
| 111 | + "respectGeminiIgnore": false |
| 112 | + } |
| 113 | + }, |
| 114 | + "fileFiltering": { |
| 115 | + "respectGitIgnore": false, |
| 116 | + "respectGeminiIgnore": false |
| 117 | + } |
| 118 | + } |
| 119 | + prompt: | |
| 120 | + Use the `jetpack-compose-performance` skill located at `.agents/skills/jetpack-compose-performance/SKILL.md` to review the newly introduced or modified Kotlin code in `.gemini/pr_diff.patch`. |
| 121 | +
|
| 122 | + Follow these instructions strictly: |
| 123 | + 1. Read `.agents/skills/jetpack-compose-performance/SKILL.md` first. |
| 124 | + 2. Read `.gemini/pr_diff.patch` to identify the exact lines added or modified in this change (`+` lines in the diff hunks). The list of touched Kotlin files is also in `.gemini/target_files.txt`. |
| 125 | + 3. You may read the touched Kotlin files listed in `.gemini/target_files.txt` ONLY to understand the surrounding context (such as enclosing `@Composable` functions, state declarations, parameters, or imports) of the changed lines. |
| 126 | + 4. **Strict Diff Scope**: Only report Jetpack Compose performance findings on **newly introduced or modified code (`+` lines in `.gemini/pr_diff.patch`)**. Do NOT report findings on pre-existing, untouched code elsewhere in the same file or in other files. |
| 127 | + 5. Whenever newly introduced or modified code matches a rule in Categories 1-7, you MUST open and read the corresponding `.agents/skills/jetpack-compose-performance/references/**/*.md` file for the approved fix pattern and scope guards before reporting a finding. |
| 128 | + 6. Do NOT modify any files in the repository and do NOT run shell commands. |
| 129 | + 7. Format your output as a Markdown review report: |
| 130 | + - Start your response with the single heading `## ⚡ Jetpack Compose Performance Review` (do not include any other top-level `#` or `##` title heading). |
| 131 | + - For each finding in the changed code, include the file path, line numbers, rule category, why it impacts Compose performance, and a concrete Kotlin `suggestion` or code snippet showing the recommended fix. |
| 132 | + - If the newly introduced or modified code follows Jetpack Compose performance best practices (or does not contain Jetpack Compose code), state clearly under the heading that no Jetpack Compose performance issues were found in the changed code. |
| 133 | +
|
| 134 | + - name: Post Findings on Pull Request |
| 135 | + if: steps.context.outputs.file_count != '0' && steps.context.outputs.pr_number != '' && steps.gemini_review.outputs.summary != '' |
| 136 | + env: |
| 137 | + GH_TOKEN: ${{ secrets.PAT || github.token }} |
| 138 | + PR_NUMBER: ${{ steps.context.outputs.pr_number }} |
| 139 | + REVIEW_SUMMARY: ${{ steps.gemini_review.outputs.summary }} |
| 140 | + run: | |
| 141 | + set -euo pipefail |
| 142 | + COMMENT_FILE="$(mktemp)" |
| 143 | + if [[ "${REVIEW_SUMMARY}" =~ ^[[:space:]]*# ]]; then |
| 144 | + printf '%s\n' "${REVIEW_SUMMARY}" > "${COMMENT_FILE}" |
| 145 | + else |
| 146 | + { |
| 147 | + echo "## ⚡ Jetpack Compose Performance Review" |
| 148 | + echo "" |
| 149 | + printf '%s\n' "${REVIEW_SUMMARY}" |
| 150 | + } > "${COMMENT_FILE}" |
| 151 | + fi |
| 152 | + gh pr comment "${PR_NUMBER}" --body-file "${COMMENT_FILE}" |
| 153 | + rm -f "${COMMENT_FILE}" |
0 commit comments