krz/domain-dig
an ios app for DNS & SSL analysis
clone: git clone https://gitbay.org/krz/domain-dig.git
v5.0.2: .github/workflows/build.yml · raw
1name: Build
2
3# Compile gate and accessibility audit for the GitHub mirror. builds.sr.ht is the
4# primary remote for this project but has no macOS images, so xcodebuild cannot
5# run there; this job builds and tests on a GitHub-hosted macOS runner instead.
6#
7# This runs `xcodebuild test`, which also compiles the app, the widget, and the
8# share extension (the DomainDig scheme's build action pulls both in as
9# dependencies). The scheme has two test targets: DomainDigUITests — an
10# accessibility audit suite; see DomainDigUITests/AccessibilityAuditHarness.swift
11# — and DomainDigTests — unit coverage of the deterministic core (report builder,
12# exporter, diff, portability dedup).
13#
14# WHAT THIS JOB IS FOR, given the audit also runs locally:
15# a clean checkout of the merge result. A local hook runs against the working
16# tree and therefore cannot catch a file that was never committed — the failure
17# mode that matters most here, since DomainDig.xcodeproj is hand-edited and uses
18# file-system-synchronized groups where a whole missing folder still builds fine
19# locally. This job is the only place that check exists; sr.ht cannot run it.
20#
21# DELIBERATELY ONE JOB, NEWEST RUNTIME ONLY. Audit coverage is not nested across
22# OS versions, so the oldest supported OS genuinely needs its own run — but the
23# macos-26 image ships only iOS 26.x runtimes, so CI *cannot* provide it. Asking
24# for two jobs here bought two near-identical 26.x runs at double the macOS
25# minutes. Floor coverage lives in Scripts/audit-a11y.sh, run from a machine that
26# actually has an 18.x runtime installed, and is wired to the pre-push hook in
27# .githooks/. See Docs/ACCESSIBILITY.md for the split.
28#
29# The audit REPORTS but does not FAIL by default. It surfaces violations that
30# exist today, so gating on it would block every unrelated PR until the
31# accessibility pass in issue #21 completes. Findings land in the job log and in
32# the uploaded .xcresult bundle, tagged [report] or [FAIL]. Enforcement is a
33# committed constant: widen `AccessibilityAuditHarness.enforcedAuditTypes` as
34# each phase clears a category. (Env vars were tried first — neither a plain
35# xcodebuild env var nor a TEST_RUNNER_-prefixed build setting reaches the UI
36# test process.)
37#
38# pull_request only, plus manual dispatch. GitHub builds the merge result (PR
39# merged into main), so a green PR validates exactly what will land on main.
40# Note: this repo currently also pushes directly to main for releases, and those
41# pushes are NOT gated here — add a `push: { branches: [main] }` trigger below if
42# you want direct-to-main commits covered too.
43#
44# paths-ignore skips prose-only changes. Both globs are single-star, so they
45# match the repo root and Docs/ but nothing deeper — a .md that ever lands inside
46# a source directory still builds.
47on:
48 pull_request:
49 paths-ignore: ['*.md', 'Docs/*.md']
50 workflow_dispatch:
51
52# The job only reads code; drop the default read-write GITHUB_TOKEN scope.
53permissions:
54 contents: read
55
56concurrency:
57 group: build-${{ github.ref }}
58 cancel-in-progress: true
59
60jobs:
61 test:
62 name: xcodebuild test
63 # macos-latest still points at macOS 15, which lacks the iOS 26+ SDK this app
64 # is built against.
65 runs-on: macos-26
66
67 steps:
68 - uses: actions/checkout@v7
69
70 - name: Show toolchain
71 run: |
72 xcodebuild -version
73 swift --version
74
75 - name: Select simulator
76 id: sim
77 run: |
78 set -euo pipefail
79
80 # Newest available iPhone runtime. No deployment-target filtering is
81 # needed for "newest" — it is always at or above the floor. The
82 # previous selector took the first iPhone from ANY runtime, which on a
83 # machine with an older runtime installed could pick a simulator below
84 # the deployment target, where the app cannot install.
85 selected=$(xcrun simctl list devices available --json \
86 | jq -c '
87 [ .devices | to_entries[]
88 | (.key | capture("SimRuntime\\.iOS-(?<maj>[0-9]+)-(?<min>[0-9]+)$")) as $v
89 | (($v.maj | tonumber) * 1000 + ($v.min | tonumber)) as $rank
90 | .value[]
91 | select(.name | startswith("iPhone"))
92 | { rank: $rank, udid: .udid, name: .name, os: "\($v.maj).\($v.min)" }
93 ]
94 | sort_by(.rank, .name)
95 | last
96 ')
97
98 if [ -z "$selected" ] || [ "$selected" = "null" ]; then
99 echo "::error::No iPhone simulator available on this image"
100 xcrun simctl list devices available >&2
101 exit 1
102 fi
103
104 label=$(echo "$selected" | jq -r '"\(.name) (iOS \(.os))"')
105 echo "Selected $label"
106 echo "udid=$(echo "$selected" | jq -r .udid)" >> "$GITHUB_OUTPUT"
107 echo "label=$label" >> "$GITHUB_OUTPUT"
108
109 - name: Test on ${{ steps.sim.outputs.label }}
110 run: |
111 set -o pipefail
112 xcodebuild test \
113 -project DomainDig.xcodeproj \
114 -scheme DomainDig \
115 -destination "id=${{ steps.sim.outputs.udid }}" \
116 -resultBundlePath TestResults.xcresult \
117 CODE_SIGNING_ALLOWED=NO
118
119 - name: Upload results
120 # Always upload: on success the bundle carries the accessibility burndown
121 # list, which is the reason this suite exists.
122 if: always()
123 uses: actions/upload-artifact@v4
124 with:
125 name: test-results
126 path: TestResults.xcresult
127 retention-days: 7