| ... | ... | @@ -175,8 +175,90 @@ jobs: |
| 175 | 175 | - name: Test resource monitor |
| 176 | 176 | run: cargo test --locked --manifest-path native/resource-monitor/Cargo.toml |
| 177 | 177 | |
| 178 | # The static analysis below needs a macOS runner, which bills ~6.7x a Linux |
| 179 | # minute, so gate it on the native sources it actually lints instead of paying |
| 180 | # for it on every push. Detection is API-only (no checkout) and fails open: if |
| 181 | # the diff cannot be resolved, the lint runs. |
| 182 | mobile_native_changes: |
| 183 | name: Mobile Native Changes |
| 184 | runs-on: blacksmith-2vcpu-ubuntu-2404 |
| 185 | timeout-minutes: 5 |
| 186 | permissions: |
| 187 | contents: read |
| 188 | pull-requests: read |
| 189 | outputs: |
| 190 | changed: ${{ steps.detect.outputs.changed }} |
| 191 | steps: |
| 192 | - name: Detect mobile native changes |
| 193 | id: detect |
| 194 | env: |
| 195 | GH_TOKEN: ${{ github.token }} |
| 196 | PR_NUMBER: ${{ github.event.pull_request.number }} |
| 197 | BEFORE_SHA: ${{ github.event.before }} |
| 198 | run: | |
| 199 | set -uo pipefail |
| 200 | |
| 201 | fail_open() { |
| 202 | echo "$* Running native static analysis." |
| 203 | echo "changed=true" >> "$GITHUB_OUTPUT" |
| 204 | exit 0 |
| 205 | } |
| 206 | |
| 207 | count_rows() { |
| 208 | printf '%s\n' "$1" | grep -c . || true |
| 209 | } |
| 210 | |
| 211 | # One row per changed file, holding the new path and, for a rename, |
| 212 | # the path it replaced: renaming a matched file out of the matched |
| 213 | # paths removes a lint input just like editing it. |
| 214 | row='[.filename, (.previous_filename // empty)] | @tsv' |
| 215 | |
| 216 | if [[ -n "${PR_NUMBER}" ]]; then |
| 217 | # The PR files endpoint stops at 3000 files and pagination cannot |
| 218 | # extend it, so cross-check against the count the PR itself reports. |
| 219 | expected=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" --jq '.changed_files') \ |
| 220 | || fail_open "Could not read the pull request." |
| 221 | rows=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files" --paginate --jq ".[] | ${row}") \ |
| 222 | || fail_open "Could not resolve changed files." |
| 223 | |
| 224 | listed=$(count_rows "$rows") |
| 225 | if [[ "$listed" -lt "$expected" ]]; then |
| 226 | fail_open "GitHub listed only ${listed} of ${expected} changed files." |
| 227 | fi |
| 228 | else |
| 229 | rows=$(gh api "repos/${GITHUB_REPOSITORY}/compare/${BEFORE_SHA}...${GITHUB_SHA}" --jq ".files[]? | ${row}") \ |
| 230 | || fail_open "Could not resolve changed files." |
| 231 | |
| 232 | # The compare endpoint reports at most 300 files and pagination does |
| 233 | # not extend that list, so a full list may be hiding native changes. |
| 234 | listed=$(count_rows "$rows") |
| 235 | if [[ "$listed" -ge 300 ]]; then |
| 236 | fail_open "GitHub listed ${listed} changed files, the compare endpoint maximum." |
| 237 | fi |
| 238 | fi |
| 239 | |
| 240 | paths=$(tr '\t' '\n' <<< "$rows") |
| 241 | |
| 242 | # Sources scripts/mobile-native-static-check.ts lints, plus the tool |
| 243 | # and rule configuration that decides how it lints them, plus the |
| 244 | # root package.json that defines the lint:mobile command. |
| 245 | pattern='^apps/mobile/.*\.(swift|kt|kts)$|^apps/mobile/(\.swiftlint\.yml|detekt\.yml|\.editorconfig|Brewfile)$|^scripts/mobile-native-static-check\.ts$|^package\.json$|^\.github/workflows/ci\.yml$' |
| 246 | |
| 247 | if grep -qE "$pattern" <<< "$paths"; then |
| 248 | echo "Native sources or lint configuration changed:" |
| 249 | grep -E "$pattern" <<< "$paths" |
| 250 | echo "changed=true" >> "$GITHUB_OUTPUT" |
| 251 | else |
| 252 | echo "No mobile native sources or lint configuration changed." |
| 253 | echo "changed=false" >> "$GITHUB_OUTPUT" |
| 254 | fi |
| 255 | |
| 178 | 256 | mobile_native_static_analysis: |
| 179 | 257 | name: Mobile Native Static Analysis |
| 258 | needs: mobile_native_changes |
| 259 | # Skip only on an explicit "no": a gate job that failed or errored leaves the |
| 260 | # output empty, and that must run the lint rather than silently skip it. |
| 261 | if: ${{ !cancelled() && needs.mobile_native_changes.outputs.changed != 'false' }} |
| 180 | 262 | runs-on: blacksmith-6vcpu-macos-26 |
| 181 | 263 | timeout-minutes: 10 |
| 182 | 264 | steps: |