-
Notifications
You must be signed in to change notification settings - Fork 0
230 lines (202 loc) · 8.78 KB
/
Copy pathci.yml
File metadata and controls
230 lines (202 loc) · 8.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
name: CI
on:
push:
branches: [main]
pull_request:
# Cancel superseded runs on the same ref.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# Detect which areas changed so jobs only run when relevant. Rust jobs run on
# core/** changes; the iOS job runs on Swift, test, Tuist, script, or binding
# (core/**) changes. A workflow edit runs everything. Docs-only changes skip
# every job.
changes:
name: Detect changes
runs-on: ubuntu-latest
outputs:
rust: ${{ steps.filter.outputs.rust }}
swift: ${{ steps.filter.outputs.swift }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- id: filter
env:
EVENT: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
BEFORE: ${{ github.event.before }}
SHA: ${{ github.sha }}
run: |
set -euo pipefail
if [[ "$EVENT" == "pull_request" ]]; then
git fetch --no-tags origin "$BASE_REF"
RANGE="origin/$BASE_REF...HEAD"
else
RANGE="$BEFORE...$SHA"
fi
FILES=$(git diff --name-only "$RANGE" 2>/dev/null || git diff --name-only HEAD~1)
echo "Changed files:"; echo "$FILES"
rust=false; swift=false
if grep -qE '^(core/|\.github/workflows/ci\.yml)' <<<"$FILES"; then rust=true; fi
if grep -qE '^(App/|Modules/|UITests/|Tuist/|scripts/|core/|Project\.swift|Tuist\.swift|\.github/workflows/ci\.yml)' <<<"$FILES"; then swift=true; fi
echo "rust=$rust" | tee -a "$GITHUB_OUTPUT"
echo "swift=$swift" | tee -a "$GITHUB_OUTPUT"
rust:
name: Rust core
runs-on: macos-26
needs: changes
if: needs.changes.outputs.rust == 'true'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
with:
components: rustfmt, clippy
- name: Cache cargo registry and target
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: core
- name: rustfmt --check
working-directory: core
run: cargo fmt --all -- --check
- name: clippy
working-directory: core
run: cargo clippy --all-targets -- -D warnings
- name: cargo test
working-directory: core
run: cargo test
# The relay-index feature is off by default (so the iOS staticlib never
# pulls in rusqlite), which means the default clippy/test steps above never
# compile the index modules. Lint and test them explicitly under the feature.
- name: clippy (relay-index feature)
working-directory: core
run: cargo clippy --all-targets --features relay-index -- -D warnings
- name: cargo test (relay-index feature)
working-directory: core
run: cargo test --features relay-index
cargo-deny:
name: cargo-deny
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.rust == 'true'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2.1.1
with:
manifest-path: core/Cargo.toml
command: check advisories bans
ios:
name: iOS app + tests
runs-on: macos-26
needs: changes
if: needs.changes.outputs.swift == 'true'
# Backstop against a hung simulator launch stalling the job for the 6h default.
timeout-minutes: 40
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Show Xcode version
run: xcodebuild -version
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
with:
targets: aarch64-apple-ios,aarch64-apple-ios-sim
- name: Cache cargo registry and target
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: core
# The XCFramework + UniFFI bindings are derived purely from core/. Cache
# them keyed on the Rust sources so iOS-only changes skip the Rust rebuild.
- name: Cache XCFramework + bindings
id: xcframework
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
Modules/CosignCore/Frameworks/CosignCore.xcframework
Modules/CosignCore/Sources/Generated
# Key on the Rust LIBRARY only: the top-level core/src/*.rs sources + the .udl,
# NOT core/src/bin/** (the relay binary). The XCFramework is built from the
# cosign_core library and does not depend on the relay binary, so relay-only
# edits must not invalidate this cache (they used to, via a core/src/** glob,
# forcing an ~18 min rebuild). If a nested library module dir is ever added
# under core/src, add it here.
key: xcframework-${{ runner.os }}-${{ hashFiles('core/src/*.rs', 'core/src/cosign_core.udl', 'core/Cargo.toml', 'core/Cargo.lock', 'core/build.rs', 'core/uniffi.toml', 'scripts/build-rust.sh', 'scripts/build-xcframework.sh') }}
- name: Build XCFramework
if: steps.xcframework.outputs.cache-hit != 'true'
run: ./scripts/build-xcframework.sh
- name: Install Tuist + SwiftLint
run: brew install tuist swiftlint
- name: Install SwiftFormat (pinned)
# Pin the formatter version. The runner image's preinstalled swiftformat
# drifts (0.62.x turned on new default rules that flag already-committed
# code), so an unpinned `brew install swiftformat` makes lint results depend
# on the image rather than the repo. Keep this in step with the version
# developers run locally.
env:
SWIFTFORMAT_VERSION: 0.61.1
run: |
curl -fsSL "https://github.com/nicklockwood/SwiftFormat/releases/download/${SWIFTFORMAT_VERSION}/swiftformat.zip" -o /tmp/swiftformat.zip
unzip -o -q /tmp/swiftformat.zip -d /tmp/swiftformat
chmod +x /tmp/swiftformat/swiftformat
echo "/tmp/swiftformat" >> "$GITHUB_PATH"
- name: swiftformat --lint
run: swiftformat --version && swiftformat --lint App/ Modules/
- name: swiftlint
run: swiftlint lint --quiet --strict App Modules
- name: Generate Xcode project
run: tuist generate --no-open
- name: Pick an available iPhone simulator
id: sim
run: |
DEVICE=$(xcrun simctl list devices available | grep -E " iPhone [0-9]+" | head -1 | sed 's/^[ \t]*//' | sed 's/ (.*//')
echo "Selected simulator: $DEVICE"
echo "device=$DEVICE" >> "$GITHUB_OUTPUT"
# Compile the app + all test bundles once, then run without rebuilding.
- name: Build for testing
env:
SIMULATOR_NAME: ${{ steps.sim.outputs.device }}
run: |
xcodebuild build-for-testing \
-workspace Cosign.xcworkspace \
-scheme Cosign-Workspace \
-destination "platform=iOS Simulator,name=$SIMULATOR_NAME,OS=latest" \
-configuration Debug
# PR/push runs the fast unit suite only. The design-screenshot UI
# walkthroughs (CosignDemoUITests) each boot the app and are slow; they run
# nightly and on demand via ui-tests.yml, off the hot path.
- name: Run unit tests
env:
SIMULATOR_NAME: ${{ steps.sim.outputs.device }}
run: |
xcodebuild test-without-building \
-workspace Cosign.xcworkspace \
-scheme Cosign-Workspace \
-destination "platform=iOS Simulator,name=$SIMULATOR_NAME,OS=latest" \
-skip-testing:CosignDemoUITests \
-retry-tests-on-failure \
-test-iterations 2
# Single aggregate check to require in branch protection. It always runs and
# passes as long as no needed job FAILED or was cancelled; a skipped job
# (path-filtered out) is fine. Requiring this one job means an irrelevant
# skipped job can never block a merge, while a real failure still does.
ci-ok:
name: CI OK
runs-on: ubuntu-latest
if: always()
needs: [changes, rust, cargo-deny, ios]
steps:
- name: Verify no required job failed
env:
RESULTS: ${{ join(needs.*.result, ' ') }}
run: |
echo "Job results: $RESULTS"
for r in $RESULTS; do
if [[ "$r" == "failure" || "$r" == "cancelled" ]]; then
echo "A CI job failed or was cancelled."
exit 1
fi
done
echo "All CI jobs succeeded or were skipped."