From 80e474db4f185910fc00f99d131f7ba58329c772 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 13 Aug 2026 17:09:59 +0200 Subject: [PATCH 1/2] chore(ci): deduplicate compiler warning annotations across the matrix Every xcodebuild job compiles the same sources, so each warning was annotated once per matrix leg, and more than once within a leg where several targets compile the same file. A single deprecation showed up ten-plus times on the same line of a pull request diff. Annotations belong to the check run that emits them, so they cannot be merged after the fact: the matrix jobs now only collect their warnings and a new `warnings` job annotates the deduplicated union once. - scripts/collect-warnings.sh parses a raw build log into deduplicated path/line/column/message records relative to the repository root, dropping diagnostics for files GitHub cannot annotate anyway (dependency checkouts, .build, .derivedData). - scripts/annotate-warnings.sh merges every job's file, annotates each unique warning once and writes the full list to the job summary, since GitHub only displays the first handful of annotations per step. - scripts/xcodebuild.sh, when WARNINGS_FILE is set, tees the raw log, filters xcbeautify's warning and notice annotations out of the live stream and appends the collected warnings to that file. Exit status is preserved and warnings are collected even when the build fails. Error annotations are left alone: those belong to the job that failed. With WARNINGS_FILE unset, behavior is unchanged for local runs. The `warnings` job is deliberately not part of `ci-success`: it reports, it does not gate. As a side effect the duplicated "Test run with N tests passed" notice annotations are gone too. --- .github/workflows/ci.yml | 55 +++++++++++++++++++++++++++++++++ AGENTS.md | 11 +++++++ dictionary.txt | 1 + scripts/annotate-warnings.sh | 59 ++++++++++++++++++++++++++++++++++++ scripts/collect-warnings.sh | 41 +++++++++++++++++++++++++ scripts/xcodebuild.sh | 32 +++++++++++++++++-- 6 files changed, 197 insertions(+), 2 deletions(-) create mode 100755 scripts/annotate-warnings.sh create mode 100755 scripts/collect-warnings.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc3e92e11..1d4d0c925 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,10 @@ jobs: name: xcodebuild (macOS latest) runs-on: blacksmith-6vcpu-macos-latest timeout-minutes: 30 + # Collect compiler warnings instead of annotating them here; the `warnings` job + # annotates the deduplicated union across the whole matrix. + env: + WARNINGS_FILE: ${{ github.workspace }}/warnings.tsv strategy: matrix: command: [test, ""] @@ -60,6 +64,14 @@ jobs: - name: Release if: matrix.skip_release != '1' run: CONFIG=Release PLATFORM="${{ matrix.platform }}" XCODEBUILD_ARGUMENT="${{ matrix.command }}" ./scripts/xcodebuild.sh + - name: Upload collected warnings + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: warnings-macos-${{ matrix.platform }}-${{ matrix.xcode }}-${{ matrix.command || 'build' }} + path: warnings.tsv + if-no-files-found: ignore + retention-days: 1 - name: Install lcov if: matrix.command == 'test' && matrix.platform == 'IOS' run: brew install lcov @@ -80,6 +92,8 @@ jobs: name: xcodebuild (legacy) runs-on: blacksmith-6vcpu-macos-15 timeout-minutes: 30 + env: + WARNINGS_FILE: ${{ github.workspace }}/warnings.tsv strategy: matrix: command: [test, ""] @@ -114,6 +128,14 @@ jobs: - name: Release if: matrix.skip_release != '1' run: CONFIG=Release PLATFORM="${{ matrix.platform }}" XCODEBUILD_ARGUMENT="${{ matrix.command }}" ./scripts/xcodebuild.sh + - name: Upload collected warnings + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: warnings-macos-legacy-${{ matrix.platform }}-${{ matrix.xcode }}-${{ matrix.command || 'build' }} + path: warnings.tsv + if-no-files-found: ignore + retention-days: 1 spm: runs-on: blacksmith-6vcpu-macos-latest @@ -242,6 +264,8 @@ jobs: name: Examples (${{ matrix.scheme }}) runs-on: blacksmith-6vcpu-macos-latest timeout-minutes: 30 + env: + WARNINGS_FILE: ${{ github.workspace }}/warnings.tsv strategy: matrix: scheme: [Examples, SlackClone, UserManagement] @@ -264,6 +288,14 @@ jobs: uses: chetan/git-restore-mtime-action@d186aca54f8760da4dec55313195e51ed3ebb0b3 # v2.3 - name: Build ${{ matrix.scheme }} run: DERIVED_DATA_PATH=~/.derivedData SCHEME="${{ matrix.scheme }}" XCODEBUILD_ARGUMENT=build ./scripts/xcodebuild.sh + - name: Upload collected warnings + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: warnings-examples-${{ matrix.scheme }} + path: warnings.tsv + if-no-files-found: ignore + retention-days: 1 examples-spm: name: Examples SPM (${{ matrix.example }}) @@ -360,6 +392,29 @@ jobs: echo "✅ All changed Swift files are properly formatted" fi + # Every xcodebuild job compiles the same sources, so letting each one annotate its own + # warnings put the same diagnostic on the same line once per matrix leg (ten-plus times + # in a pull request diff, twice more where a file is compiled for several targets). The + # matrix jobs now only collect their warnings; this job annotates the union of them once. + # It is deliberately not part of `ci-success`: it reports, it does not gate. + warnings: + name: Compiler warnings + if: always() + needs: [macos, macos-legacy, examples] + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Download collected warnings + # No artifacts at all (every build job died early) is not an error here. + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + path: collected-warnings + pattern: warnings-* + - name: Annotate deduplicated warnings + run: ./scripts/annotate-warnings.sh collected-warnings + ci-success: name: CI Success if: always() diff --git a/AGENTS.md b/AGENTS.md index 03f478262..b7295638a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -260,6 +260,17 @@ Tests run on: Coverage is automatically generated for iOS tests on the main CI job and uploaded to Coveralls. +### Compiler Warnings + +Every `xcodebuild` job compiles the same sources, so a single warning would otherwise be +annotated once per matrix leg. Instead, when `WARNINGS_FILE` is set, `scripts/xcodebuild.sh` +suppresses xcbeautify's warning annotations and appends the warnings to that file via +`scripts/collect-warnings.sh`. Each job uploads the file as a `warnings-*` artifact, and the +`warnings` job merges them and calls `scripts/annotate-warnings.sh`, which annotates each +unique `file:line:column:message` exactly once and writes the full list to the job summary. + +Locally, with `WARNINGS_FILE` unset, `scripts/xcodebuild.sh` behaves as before. + ## Support Policy - **Xcode**: Only versions eligible for App Store submission diff --git a/dictionary.txt b/dictionary.txt index 634d2247d..0bfd8c4c1 100644 --- a/dictionary.txt +++ b/dictionary.txt @@ -211,6 +211,7 @@ wfts workos WorkOS Wwta +xcbeautify xcodeproj xctest XVCJ diff --git a/scripts/annotate-warnings.sh b/scripts/annotate-warnings.sh new file mode 100755 index 000000000..c1132c06a --- /dev/null +++ b/scripts/annotate-warnings.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# Emits one GitHub Actions warning annotation per unique compiler warning, merging the +# per-job files produced by scripts/collect-warnings.sh. +# +# The build matrix compiles the same sources for every platform, Xcode version and +# configuration, so letting each job annotate its own warnings puts the same diagnostic on +# the same line ten-plus times in the pull request diff. Instead the matrix jobs only +# collect, and this script (running once, in the `warnings` job) annotates the union. +# +# Usage: scripts/annotate-warnings.sh +set -euo pipefail + +DIRECTORY="${1?usage: annotate-warnings.sh }" + +MERGED="$(mktemp)" +trap 'rm -f "$MERGED"' EXIT + +if [[ -d "$DIRECTORY" ]]; then + find "$DIRECTORY" -type f -name '*.tsv' -exec cat {} + | sort -u -o "$MERGED" || true +fi + +COUNT=$(wc -l <"$MERGED" | tr -d ' ') + +summary() { + if [[ -n "${GITHUB_STEP_SUMMARY-}" ]]; then + cat >>"$GITHUB_STEP_SUMMARY" + else + cat + fi +} + +if [[ "$COUNT" -eq 0 ]]; then + echo "No compiler warnings collected." + echo "### ✅ No compiler warnings" | summary + exit 0 +fi + +# GitHub only surfaces the first handful of annotations per step, so the summary carries +# the full list. +{ + echo "### ⚠️ $COUNT unique compiler warning(s)" + echo + echo "| File | Line | Warning |" + echo "| --- | --- | --- |" + while IFS=$'\t' read -r path line column message; do + echo "| \`$path\` | $line | ${message//|/\\|} |" + done <"$MERGED" + echo + echo "Annotations shown in the diff are deduplicated across the whole build matrix." +} | summary + +while IFS=$'\t' read -r path line column message; do + # Percent, carriage return and newline are the characters GitHub's workflow command + # parser treats specially in an annotation message. + escaped="${message//\%/%25}" + escaped="${escaped//$'\r'/%0D}" + escaped="${escaped//$'\n'/%0A}" + echo "::warning file=$path,line=$line,col=$column::$escaped" +done <"$MERGED" diff --git a/scripts/collect-warnings.sh b/scripts/collect-warnings.sh new file mode 100755 index 000000000..6b7c7c122 --- /dev/null +++ b/scripts/collect-warnings.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Extracts compiler warnings from a raw xcodebuild / swift build log and writes them to +# stdout as `pathlinecolumnmessage` records, deduplicated. +# +# A single build reports the same warning once per target that compiles the file, so even +# one log needs deduplicating. scripts/annotate-warnings.sh does the same across logs. +# +# Warnings for files outside the repository (dependency checkouts, DerivedData) are +# dropped: GitHub cannot annotate a file that is not part of the checkout, and they are +# not actionable here anyway. +# +# Usage: scripts/collect-warnings.sh +set -euo pipefail + +LOG="${1?usage: collect-warnings.sh }" +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +# xcbeautify colorizes its output, and warnings emitted by tools other than the compiler +# can carry escape sequences too, so strip them before matching. +sed -E $'s/\x1b\\[[0-9;]*[A-Za-z]//g' "$LOG" | awk -v root="$ROOT" ' + { + sub(/^[[:space:]]+/, "") + } + match($0, /:[0-9]+:[0-9]+: warning: /) { + path = substr($0, 1, RSTART - 1) + if (index(path, root "/") != 1) next + + location = substr($0, RSTART + 1) + split(location, parts, ":") + message = substr($0, RSTART + RLENGTH) + + relative = substr(path, length(root) + 2) + if (relative ~ /^\.build\// || relative ~ /^\.derivedData\//) next + + key = relative "\t" parts[1] "\t" parts[2] "\t" message + if (!(key in seen)) { + seen[key] = 1 + print key + } + } +' diff --git a/scripts/xcodebuild.sh b/scripts/xcodebuild.sh index 3879659be..a7a6fcf15 100755 --- a/scripts/xcodebuild.sh +++ b/scripts/xcodebuild.sh @@ -7,6 +7,9 @@ # WORKSPACE Xcode workspace (default: Supabase.xcworkspace) # XCODEBUILD_ARGUMENT xcodebuild action, e.g. build | test (default: test) # DERIVED_DATA_PATH (default: ~/.derivedData/$CONFIG) +# WARNINGS_FILE when set, compiler warnings are appended to this file instead of +# being annotated here, and the `warnings` CI job annotates the +# deduplicated union of every job's file (see collect-warnings.sh) set -euo pipefail CONFIG="${CONFIG-Debug}" @@ -54,8 +57,33 @@ if [[ -n "$XCODEBUILD_ARGUMENT" ]]; then fi XCODEBUILD_ARGS+=("${XCODEBUILD_FLAGS[@]}") +RAW_LOG=/dev/null +if [[ -n "${WARNINGS_FILE-}" ]]; then + RAW_LOG="$(mktemp)" + trap 'rm -f "$RAW_LOG"' EXIT +fi + +beautify() { + if [[ -n "${WARNINGS_FILE-}" ]]; then + # xcbeautify annotates every warning it sees, which the matrix then repeats once per + # platform, Xcode version and configuration. Drop its warning and notice annotations + # and let the `warnings` job annotate the deduplicated union instead. Error + # annotations are kept: those belong to the job that failed. + xcbeautify | grep --line-buffered -Ev '^::(warning|notice)' || true + else + xcbeautify + fi +} + +STATUS=0 if command -v xcbeautify >/dev/null 2>&1; then - xcodebuild "${XCODEBUILD_ARGS[@]}" | xcbeautify + xcodebuild "${XCODEBUILD_ARGS[@]}" | tee "$RAW_LOG" | beautify || STATUS=$? else - xcodebuild "${XCODEBUILD_ARGS[@]}" + xcodebuild "${XCODEBUILD_ARGS[@]}" | tee "$RAW_LOG" || STATUS=$? fi + +if [[ -n "${WARNINGS_FILE-}" ]]; then + "$(dirname "${BASH_SOURCE[0]}")/collect-warnings.sh" "$RAW_LOG" >>"$WARNINGS_FILE" +fi + +exit "$STATUS" From b41bcfa9768a752b78e780c18c2ef7b44c59e16d Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 13 Aug 2026 17:37:06 +0200 Subject: [PATCH 2/2] chore(ci): drop the AGENTS.md warning-collection note The scripts already document the flow in their own headers, and xcodebuild.sh documents WARNINGS_FILE, so the section only restated CI plumbing that agents do not act on. Reverts the dictionary entry it needed too. --- AGENTS.md | 11 ----------- dictionary.txt | 1 - 2 files changed, 12 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b7295638a..03f478262 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -260,17 +260,6 @@ Tests run on: Coverage is automatically generated for iOS tests on the main CI job and uploaded to Coveralls. -### Compiler Warnings - -Every `xcodebuild` job compiles the same sources, so a single warning would otherwise be -annotated once per matrix leg. Instead, when `WARNINGS_FILE` is set, `scripts/xcodebuild.sh` -suppresses xcbeautify's warning annotations and appends the warnings to that file via -`scripts/collect-warnings.sh`. Each job uploads the file as a `warnings-*` artifact, and the -`warnings` job merges them and calls `scripts/annotate-warnings.sh`, which annotates each -unique `file:line:column:message` exactly once and writes the full list to the job summary. - -Locally, with `WARNINGS_FILE` unset, `scripts/xcodebuild.sh` behaves as before. - ## Support Policy - **Xcode**: Only versions eligible for App Store submission diff --git a/dictionary.txt b/dictionary.txt index 0bfd8c4c1..634d2247d 100644 --- a/dictionary.txt +++ b/dictionary.txt @@ -211,7 +211,6 @@ wfts workos WorkOS Wwta -xcbeautify xcodeproj xctest XVCJ