ci: add Option B failure summary + Option A artifact fallback; tee bu… #12
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: CI | |
on: | |
push: | |
branches: [ dev, feature/**, bugfix/**, chore/** ] | |
pull_request: | |
branches: [ dev ] | |
jobs: | |
build: | |
runs-on: macos-14 | |
env: | |
SCHEME: Avro Keyboard # Default equals target name in this repo | |
TARGET: Avro Keyboard # Fallback target if no shared scheme | |
CONFIG: Debug | |
LOG_DIR: ci-logs # Directory for CI logs and summaries | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Xcode version | |
run: | | |
xcodebuild -version | |
sw_vers | |
- name: Prepare logs directory | |
run: | | |
rm -rf "$LOG_DIR" && mkdir -p "$LOG_DIR" | |
- name: Install xcpretty | |
run: | | |
gem install xcpretty --no-document | |
- name: Install CocoaPods | |
run: | | |
gem install cocoapods --no-document | |
- name: Install Subversion (for RegexKitLite) | |
run: | | |
brew update | |
brew install subversion | |
- name: Pod install | |
run: | | |
pod install --repo-update --silent || pod install | |
- name: Detect project or workspace | |
shell: bash | |
run: | | |
set -euo pipefail | |
ROOT=$(pwd -P) | |
WS="AvroKeyboard.xcworkspace" | |
PRJ="AvroKeyboard.xcodeproj" | |
if [[ -d "$WS" && -d "Pods" && -f "Pods/Pods.xcodeproj/project.pbxproj" ]]; then | |
echo "KIND=workspace" >> "$GITHUB_ENV" | |
echo "WORKSPACE=$ROOT/$WS" >> "$GITHUB_ENV" | |
elif [[ -d "$PRJ" ]]; then | |
echo "KIND=project" >> "$GITHUB_ENV" | |
echo "PROJECT=$ROOT/$PRJ" >> "$GITHUB_ENV" | |
else | |
echo "No Xcode workspace or project found"; ls -la; exit 1 | |
fi | |
- name: Resolve scheme | |
shell: bash | |
run: | | |
set -euo pipefail | |
export LC_ALL=C | |
WANT_SCHEME="${SCHEME:-}" | |
list_cmd() { | |
if [[ "${KIND:-}" == "workspace" ]]; then | |
xcodebuild -list -workspace "${WORKSPACE}" 2>/dev/null | |
else | |
xcodebuild -list -project "${PROJECT}" 2>/dev/null | |
fi | |
} | |
LIST_TRIMMED=$(list_cmd \ | |
| sed -n '/Schemes:/,$p' \ | |
| tail -n +2 \ | |
| sed 's/\r$//' \ | |
| awk '{gsub(/^[ \t]+|[ \t]+$/,""); if (length) print}') | |
echo "Available schemes:"; printf '%s\n' "$LIST_TRIMMED" | |
SEL="" | |
WANT_SCHEME_TRIM=$(printf '%s' "$WANT_SCHEME" | awk '{gsub(/^[ \t]+|[ \t]+$/,""); print}') | |
if [[ -n "$WANT_SCHEME_TRIM" ]] && printf '%s\n' "$LIST_TRIMMED" | grep -Fxq "$WANT_SCHEME_TRIM"; then | |
SEL="$WANT_SCHEME_TRIM" | |
else | |
SEL=$(printf '%s\n' "$LIST_TRIMMED" | head -n1) | |
fi | |
if [[ -z "$SEL" ]]; then | |
echo "No shared schemes found"; exit 1 | |
fi | |
echo "Resolved scheme: '$SEL'" | |
echo "SCHEME_RESOLVED=$SEL" >> "$GITHUB_ENV" | |
- name: Build | |
shell: bash | |
run: | | |
set -euo pipefail | |
if [[ "${KIND:-}" == "workspace" ]]; then | |
xcodebuild \ | |
-workspace "${WORKSPACE}" \ | |
-scheme "${SCHEME_RESOLVED}" \ | |
-configuration "$CONFIG" \ | |
-destination 'platform=macOS' \ | |
CODE_SIGNING_ALLOWED=NO \ | |
build 2>&1 | tee "$LOG_DIR/build.log" | xcpretty | |
else | |
xcodebuild \ | |
-project "${PROJECT}" \ | |
-scheme "${SCHEME_RESOLVED}" \ | |
-configuration "$CONFIG" \ | |
-destination 'platform=macOS' \ | |
CODE_SIGNING_ALLOWED=NO \ | |
build 2>&1 | tee "$LOG_DIR/build.log" | xcpretty || { | |
echo "Scheme build failed; retrying with target '$TARGET'"; | |
xcodebuild \ | |
-project "${PROJECT}" \ | |
-target "$TARGET" \ | |
-configuration "$CONFIG" \ | |
-destination 'platform=macOS' \ | |
CODE_SIGNING_ALLOWED=NO \ | |
build 2>&1 | tee -a "$LOG_DIR/build.log" | xcpretty; | |
} | |
fi | |
env: | |
NSUnbufferedIO: "YES" | |
continue-on-error: false | |
- name: Run unit tests (if any) | |
shell: bash | |
run: | | |
set -euo pipefail | |
HAS_TESTS=$( { grep -R --include='*.m' --include='*.swift' -n "XCTestCase" . || true; } | wc -l | tr -d '[:space:]') | |
if [[ "$HAS_TESTS" -gt "0" ]]; then | |
if [[ "${KIND:-}" == "workspace" ]]; then | |
# Only run tests with a scheme; if no shared scheme, skip. | |
if xcodebuild -list -workspace "${WORKSPACE}" | grep -q "Schemes:"; then | |
xcodebuild \ | |
-workspace "${WORKSPACE}" \ | |
-scheme "${SCHEME_RESOLVED}" \ | |
-configuration "$CONFIG" \ | |
-destination 'platform=macOS' \ | |
CODE_SIGNING_ALLOWED=NO \ | |
test 2>&1 | tee "$LOG_DIR/test.log" | xcpretty | |
else | |
echo "No shared scheme detected; skipping XCTest run." | |
fi | |
else | |
if xcodebuild -list -project "${PROJECT}" | grep -q "Schemes:"; then | |
xcodebuild \ | |
-project "${PROJECT}" \ | |
-scheme "${SCHEME_RESOLVED}" \ | |
-configuration "$CONFIG" \ | |
-destination 'platform=macOS' \ | |
CODE_SIGNING_ALLOWED=NO \ | |
test 2>&1 | tee "$LOG_DIR/test.log" | xcpretty | |
else | |
echo "No shared scheme detected; skipping XCTest run." | |
fi | |
fi | |
else | |
echo "No XCTest targets detected; skipping test step." | |
fi | |
- name: Summarize failure (Option B) | |
if: failure() | |
shell: bash | |
run: | | |
set -euo pipefail | |
SUMMARY="$LOG_DIR/ci_summary.txt" | |
{ | |
echo "CI Failure Summary" | |
echo "Kind: ${KIND:-unknown}" | |
echo "Workspace: ${WORKSPACE:-n/a}" | |
echo "Project: ${PROJECT:-n/a}" | |
echo "Scheme: ${SCHEME_RESOLVED:-n/a}" | |
echo | |
if [[ -f "$LOG_DIR/build.log" ]]; then | |
echo "=== Build Errors (top hits) ===" | |
grep -nE "xcodebuild: error:|\\berror:|Undefined symbols|Command PhaseScriptExecution failed|exit code 65|ld: error" "$LOG_DIR/build.log" | head -n 100 || true | |
echo | |
echo "=== Build Log Tail (200 lines) ===" | |
tail -n 200 "$LOG_DIR/build.log" || true | |
echo | |
else | |
echo "No build.log captured." | |
fi | |
if [[ -f "$LOG_DIR/test.log" ]]; then | |
echo "=== Test Errors (top hits) ===" | |
grep -nE "\\berror:|failing|Assertion failed|Test Failed|Failures:" "$LOG_DIR/test.log" | head -n 100 || true | |
echo | |
echo "=== Test Log Tail (200 lines) ===" | |
tail -n 200 "$LOG_DIR/test.log" || true | |
echo | |
fi | |
} > "$SUMMARY" | |
echo "## CI Failure Summary" >> "$GITHUB_STEP_SUMMARY" | |
echo "" >> "$GITHUB_STEP_SUMMARY" | |
echo '\`\`\`' >> "$GITHUB_STEP_SUMMARY" | |
tail -n 200 "$SUMMARY" >> "$GITHUB_STEP_SUMMARY" || true | |
echo '\`\`\`' >> "$GITHUB_STEP_SUMMARY" | |
- name: Upload logs (Option A fallback) | |
if: failure() | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ci-logs | |
path: | | |
ci-logs/** | |
- name: Regression tests (optional; skip if file missing) | |
shell: bash | |
run: | | |
if [[ -f "Tests/Regression/phrases.tsv" && -x "Tools/run_regression.sh" ]]; then | |
Tools/run_regression.sh Tests/Regression/phrases.tsv | |
else | |
echo "Regression harness not present yet; skipping." | |
fi |