Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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, ""]
Expand Down Expand Up @@ -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
Expand All @@ -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, ""]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand All @@ -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 }})
Expand Down Expand Up @@ -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()
Expand Down
59 changes: 59 additions & 0 deletions scripts/annotate-warnings.sh
Original file line number Diff line number Diff line change
@@ -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 <directory-with-collected-warnings>
set -euo pipefail

DIRECTORY="${1?usage: annotate-warnings.sh <directory>}"

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"
41 changes: 41 additions & 0 deletions scripts/collect-warnings.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Extracts compiler warnings from a raw xcodebuild / swift build log and writes them to
# stdout as `path<TAB>line<TAB>column<TAB>message` 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 <log-file>
set -euo pipefail

LOG="${1?usage: collect-warnings.sh <log-file>}"
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
}
}
'
32 changes: 30 additions & 2 deletions scripts/xcodebuild.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -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"
Loading